#Authors: Ian McElveen and Cecilia Gonzales
#Author Date: 7/14/2025
#Purpose: The purpose of this notebook is to house all data set transformation, cleansing, visualization, statistical analysis, and note-taking for the 2025 CU Athletic Department Sports Science Internship Program

#LAST UPDATED: 7/29/2025

#Including helpful libraries
library(tidyverse)
library(readxl)
library(aod)
library(gt)
library(boot)
library(mgcv)
library(lme4)
library(leaps)

Data Cleaning

#loading in the Catapult data to look at sprinting values
Catapult_Session <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Catapult Session - Outdoor FB.csv")

#loading in the Historical Running data to look at running imbalance values
Historical_Running <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Compiled Historical Running Imbalance FB.csv")

#loading in the Incident Report to look at HSIs
Incident_Report <- read_csv("data-sets/data-sets-uncompressed/data-sets-compressed/Running Imbalance and Speed/Incident Report FB IDs.csv")
Catapult_Session_clean <- Catapult_Session %>%
  #putting the date as a date class
  mutate(Date = as.Date(Date, "%m/%d/%Y")) %>%
  #only selecting important columns for this analysis
  select(anon_id, Date, Age, Primary.Position, Total.Distance, Period.Name, Total.Duration..min., Velocity.Band.1.Total.Distance, Velocity.Band.2.Total.Distance, Velocity.Band.3.Total.Distance, Velocity.Band.4.Total.Distance, Velocity.Band.5.Total.Distance, Velocity.Band.6.Total.Distance, Velocity.Band.7.Total.Distance, Velocity.Band.8.Total.Distance, Velocity.Band.2.Total.Effort.Count, Velocity.Band.3.Total.Effort.Count, Velocity.Band.4.Total.Effort.Count, Velocity.Band.5.Total.Effort.Count, Velocity.Band.6.Total.Effort.Count, Velocity.Band.7.Total.Effort.Count, Velocity.Band.8.Total.Effort.Count, Maximum.Velocity, Average.Velocity, Hit.90.Percent.Max, Date.of.Last.90.Effort, Days.Since.Last.90.Effort, Hit.Max.Velocity., Date.of.All.Time.Max.Velocity, Days.Since.Max.Velocity, Session.Max.Velocity) %>%
  #calculating each player's maximum velocity
  group_by(anon_id) %>%
  mutate(Player.Max.Velocity = max(na.omit(Maximum.Velocity))) %>%
  ungroup() %>%
  #only selecting data from January 1, 2024 and on
  filter(Date >= "2024-01-01")

head(Catapult_Session_clean)
Historical_Running_clean <- Historical_Running %>%
  #taking out rows that don't have data
  filter(Running.Imbalance != "n/a") %>%
  #putting running imbalance as a number and converting the date to a date class
  mutate(Running.Imbalance = as.numeric(Running.Imbalance),
         Date = as.Date(Date, "%m/%d/%Y")) %>%
  #only using data from January 1, 2024 and on
  filter(Date >= "2024-01-01") %>%
  mutate(X=1:4063) %>%
  #making days since January 1, 2024 for each player
  group_by(anon_id) %>%
  mutate(Days.Since.Start = as.numeric(Date - min(Date))) %>%
  ungroup()

head(Historical_Running_clean)
Incident_Report_clean <- Incident_Report %>%
  #filtering for only hamstring injuries
  filter(OSICS14.Code == "TM1",
         Status != "Full Go")  %>%
  #getting the date of the injury as a date class
  mutate(Date = as.Date(Date, "%m/%d/%Y"),
         Date.of.Injury = as.Date(Date.of.Injury...Onset.of.symptoms, "%m/%d/%Y"),
         Examination.Date = as.Date(Examination.Date, "%m/%d/%Y")) %>%
  #only selecting relevant columns for this analysis
  select(anon_id, Position, Date, Date.of.Injury, Time.of.Injury, Side, OSICS.Injury.Diagnosis, Coach.s.Diagnosis, Recurrence.of.Injury, Choose.Season, Onset.of.Symptoms, Injury.Prognosis, General.Mechanism, Specific.Mechanism, Injured.While., Type.of.Event, Season., Status, Days.in.Status) %>%
  #making days out due to injury for each player and each injury they sustained
  group_by(anon_id, Date.of.Injury) %>%
  mutate(Days.Out = sum(Days.in.Status)) %>%
  ungroup()

head(Incident_Report_clean)
#taking the IDs of players who are and aren't injured
all_IDs <- unique(Historical_Running_clean$anon_id)
#taking IDs that were injured and also have running imbalance data
injured_IDs <- intersect(unique(Incident_Report_clean$anon_id), all_IDs)
#taking all players with running imbalance data that don't have an injury
uninjured_IDs <- unique((Historical_Running_clean %>%
  filter(!anon_id %in% injured_IDs))$anon_id)
#injured players who also have running imbalance data
Incident_Report_clean <- Incident_Report_clean %>%
  filter(anon_id %in% injured_IDs)

#all players that only have running imbalance data or have both running imbalance data and incidence report
Historical_Running_clean <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs | anon_id %in% uninjured_IDs)
#removing uncleaned data sets
remove(Incident_Report)
remove(Historical_Running)
remove(Catapult_Session)

Section 1: Running Speed

How often are athletes reaching ≥ 90% maximum velocity throughout a training season?

# Bar chart for how often players reach ≥ 90% maximum velocity

# Count for how many times each anon_id hit ≥ 90% maximum velocity
hit_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>% # Filter for training season
  filter(Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date, Primary.Position) %>%
  group_by(anon_id, Primary.Position) %>%
  summarise(times_hit_90 = n(), .groups = "drop") %>%
  mutate(
    Position_Group = case_when(
      Primary.Position %in% c("QB", "LB", "TE", "RB") ~ "COMBO",
      Primary.Position %in% c("OL", "DL", "DE") ~ "BIGS",
      Primary.Position %in% c("WR", "DB", "DB, WR") ~ "SKILL",
      TRUE ~ "OTHER"
    ))

# Plot of all players' frequencies
ggplot(hit_90_counts, aes(x = anon_id, y = times_hit_90)) +
  geom_bar(stat = "identity", fill = "#CFB87C") +
  geom_hline(yintercept = mean(hit_90_counts$times_hit_90), 
             linetype = "dashed", color = "#565A5C", linewidth = 0.5) +
  labs(title = "Player Counts for Achieving ≥ 90% of Maximum Velocity During 2024–25 Season", 
       subtitle = paste("Team Average:", round(mean(hit_90_counts$times_hit_90), 1)),
       x = "Athlete ID", y = "Times ≥ 90%") +
  theme_classic() +
  theme(axis.text.x = element_text(size = 6, angle = 90))

Do not need because we created a function to plot each position

# Bar chart of times reached >90%, Quarterbacks

# Filter to only have data for QBs
QBs <- hit_90_counts %>%
  filter(Primary.Position == "QB")

# Calculate the averages first
overall_avg <- mean(hit_90_counts$times_hit_90)
qb_avg <- mean(QBs$times_hit_90)

ggplot(QBs, aes(x=anon_id, y=times_hit_90)) +
  geom_bar(stat="identity", fill = "#CFB87C") + 
  geom_text(aes(label = times_hit_90), 
            vjust = -0.5, 
            size = 3.5) +
  geom_hline(yintercept = overall_avg, 
             linetype = "dashed", color = "#000000") +
  geom_hline(yintercept = qb_avg, 
             linetype = "dashed", color = "#565A5C") +
  annotate("text", x = 1, y = overall_avg + 0.5, 
           label = "Team Avg", color = "#000000", size = 3) +
  annotate("text", x = 1, y = qb_avg + 0.5, 
           label = "QB Avg", color = "#565A5C", size = 3) +
  labs(title = "QB Counts for Reaching ≥90% Max Velocity", 
       x = "Athlete ID", y = "Times ≥90%",
       subtitle = paste("QB Average:", qb_avg)) +
  theme_classic()

Facet plot useful, but hard to read so probably don’t include in presentation.

# Facet Plot

# Calculate overall average
overall_avg <- mean(hit_90_counts$times_hit_90)

# Plot faceted bar charts by position
ggplot(hit_90_counts, aes(x = anon_id, y = times_hit_90)) +
  geom_bar(stat = "identity", fill = "#CFB87C") +
  geom_text(aes(label = times_hit_90), vjust = -0.5, size = 3.5) +
  geom_hline(yintercept = overall_avg, linetype = "dashed", color = "#000000") +
  annotate("text", x = 1, y = overall_avg + 0.5, 
           label = "Team Avg", color = "#000000", size = 3, hjust = 0) +
  facet_wrap(~ Primary.Position, scales = "free_x") +
  labs(title = "Counts of ≥90% Max Velocity by Player and Position",
       y = "Times ≥90% Max Velocity",
       x = "Athlete ID") +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Plots for each position

# Get the overall team average once from hit_90_counts
team_avg <- mean(hit_90_counts$times_hit_90)

# Define  positions to loop through
positions <- unique(hit_90_counts$Primary.Position)

# Plotting function:
plot_hit_90_by_position <- function(pos) {
  position_data <- hit_90_counts %>%
    filter(Primary.Position == pos)
  
  pos_avg <- mean(position_data$times_hit_90)
  
  ggplot(position_data, aes(x = anon_id, y = times_hit_90)) +
    geom_bar(stat = "identity", fill = "#CFB87C") +
    geom_text(aes(label = times_hit_90), vjust = -0.5, size = 3.5) +
    geom_hline(yintercept = pos_avg, linetype = "dashed", color = "#565A5C") +
    annotate("text", x = 1, y = pos_avg + 0.5, 
             label = "Pos Avg", color = "#565A5C", size = 3, hjust = 0) +
    geom_hline(yintercept = team_avg, linetype = "dashed", color = "#000000") +
    annotate("text", x = 1, y = team_avg + 0.5, 
             label = "Team Avg", color = "#000000", size = 3, hjust = 0) +
    labs(title = paste("Times ≥90% Max Velocity –", pos),
         subtitle = paste0("Position Avg: ", round(pos_avg, 1),
                           " | Team Avg: ", round(team_avg, 1)),
         y = "Count",
         x = "anon_id") +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

# Loop through each position and print the plot
plots_by_position <- lapply(positions, function(pos) {
  print(plot_hit_90_by_position(pos))
})
# Table for average values of each position

position_averages <- hit_90_counts %>%
  group_by(Primary.Position) %>%
  summarise(avg_times_hit_90 = mean(times_hit_90), .groups = "drop") %>%
  arrange(desc(avg_times_hit_90))

position_averages_with_team <- bind_rows(
  tibble(
    Primary.Position = "Team Average",
    avg_times_hit_90 = team_avg
  ),
  position_averages
)

position_averages_with_team %>%
  gt() %>%
  tab_header(
    title = "Average Times ≥90% Max Velocity by Position and Team"
  )

DBs have the highest average times reaching ≥90% of Max velocity (18.4). The OL had the lowest (6.8). Team average was 11.8.

# Plot counts for each position group

# Get the overall team average once from hit_90_counts
team_avg <- mean(hit_90_counts$times_hit_90)

# Define  positions to loop through
positions_groups <- unique(hit_90_counts$Position_Group)

# Plotting function:
plot_hit_90_group <- function(pos) {
  group_data <- hit_90_counts %>%
    filter(Position_Group == pos)
  
  group_avg <- mean(group_data$times_hit_90)
  
  ggplot(group_data, aes(x = anon_id, y = times_hit_90)) +
    geom_bar(stat = "identity", fill = "#CFB87C") +
    geom_text(aes(label = times_hit_90), vjust = -0.5, size = 3.5) +
    geom_hline(yintercept = group_avg, linetype = "dashed", color = "#565A5C") +
    annotate("text", x = 1, y = group_avg + 0.5, 
             label = "Group Avg", color = "#565A5C", size = 3, hjust = 0) +
    geom_hline(yintercept = team_avg, linetype = "dashed", color = "#000000") +
    annotate("text", x = 1, y = team_avg + 0.5, 
             label = "Team Avg", color = "#000000", size = 3, hjust = 0) +
    labs(title = paste("Times ≥90% Max Velocity –", pos),
         subtitle = paste0("Group Avg: ", round(group_avg, 1),
                           " | Team Avg: ", round(team_avg, 1)),
         y = "Count",
         x = "anon_id") +
    theme_classic() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
}

# Loop through each position and print the plot
plots_by_group <- lapply(positions_groups, function(pos) {
  print(plot_hit_90_group(pos))
})

The skill position group has the highest average times reached ≥90% Max with 17.1, then combo with 9.3, then bigs with 8.9.

# Create position group avg count table

# Create team average row
team_row <- tibble(
  Group = "Team Avg",
  Average = team_avg
)

# Create group average rows
group_rows <- hit_90_counts %>%
  group_by(Position_Group) %>%
  summarise(Average = mean(times_hit_90), .groups = "drop") %>%
  arrange(desc(Average)) %>%  # Sort from highest to lowest
  rename(Group = Position_Group)

# Combine them (team first)
combined_table <- bind_rows(team_row, group_rows)

# View the table
combined_table

No surprise that the skill position group has the highest average counts.

# >90% counts over the course of the season

# Trying to find out when are players reaching above 90% the most

# Create dataset that has the count of >90% of each day
daily_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
  filter(Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date) %>%
  group_by(Date) %>%
  summarise(daily_hits = n())

# Plot
ggplot(daily_90_counts, aes(x = Date, y = daily_hits)) +
  geom_line(color = "#CFB87C", linewidth = 1) +
  geom_smooth(method = "loess", se = FALSE, color = "#565A5C", linetype = "dashed") +
  labs(title = "Daily Count of Players Reaching ≥ 90% of Max Velocity",
       subtitle = "Over the 2024–25 Training Season",
       x = "Date", y = "Times reached ≥ 90%") +
  theme_classic()

Data is noisy, so lets try grouping by week instead of every day.

# >90% counts for each week instead of each day. Will be a little less noisy than the daily
weekly_90_counts <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
  filter(Hit.90.Percent.Max == "Yes") %>%
  distinct(anon_id, Date) %>%
  mutate(week = floor_date(Date, unit = "week")) %>%
  group_by(week) %>%
  summarise(weekly_hits = n())

ggplot(weekly_90_counts, aes(x = week, y = weekly_hits)) +
  geom_line(color = "#CFB87C", linewidth = 1) +
  geom_point(color = "#565A5C") +
  geom_hline(yintercept = mean(weekly_90_counts$weekly_hits), 
             linetype = "dashed", color = "#565A5C", linewidth = 0.6) +
  labs(title = "Weekly Count of Players Reaching ≥ 90% of Max Velocity",
       x = "Week", y = "Times reached ≥ 90%") +
  theme_classic()
# Bar chart of weekly >90% counts

mean_val <- mean(weekly_90_counts$weekly_hits)

ggplot(weekly_90_counts, aes(x = week, y = weekly_hits)) +
  geom_col(fill = "#CFB87C", color = "#565A5C", width = 5) +
  geom_text(aes(label = weekly_hits), 
            vjust = -0.4, 
            size = 2.5, 
            color = "black") +
  geom_hline(yintercept = mean_val,
             linetype = "dashed", color = "#565A5C", linewidth = 0.5) +
  annotate("text", 
           x = min(weekly_90_counts$week) + 7,  # Adjust this date to place the label
           y = mean_val + 1.5,                 # Slightly above the line
           label = paste("Mean:", round(mean_val, 1)),
           size = 3.25, color = "#000000") +
  labs(
    title    = "Weekly Count of Players Reaching ≥ 90% of Max Velocity",
    subtitle = paste("Season Average per Week:", round(mean(weekly_90_counts$weekly_hits), 1)),
    x        = "Week",
    y        = "Times reached ≥ 90%"
  ) +
  scale_x_date(date_breaks = "2 weeks", date_labels = "%b %d") +
  theme_classic() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

Some of our lowest average counts for a week seem to be during the season, whereas some of our highest counts seem to be pre and post-season.

Should we consider the number of sprinting efforts that athletes are completing?

How often athletes are hitting which velocity bands in relation to their top speeds.

# Create sum of weekly band totals

# Make sure week is defined
Catapult_Session_clean <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>% 
  mutate(week = floor_date(Date, unit = "week"))

# Sum efforts by week and athlete
weekly_velocity_efforts <- Catapult_Session_clean %>%
  group_by(anon_id, week) %>%
  summarise(
    V2 = sum(Velocity.Band.2.Total.Effort.Count, na.rm = TRUE),
    V3 = sum(Velocity.Band.3.Total.Effort.Count, na.rm = TRUE),
    V4 = sum(Velocity.Band.4.Total.Effort.Count, na.rm = TRUE),
    V5 = sum(Velocity.Band.5.Total.Effort.Count, na.rm = TRUE),
    V6 = sum(Velocity.Band.6.Total.Effort.Count, na.rm = TRUE),
    V7 = sum(Velocity.Band.7.Total.Effort.Count, na.rm = TRUE),
    V8 = sum(Velocity.Band.8.Total.Effort.Count, na.rm = TRUE),
    total_efforts = V2 + V3 + V4 + V5 + V6 + V7 + V8,
    Weekly_Max_Velocity = max(Maximum.Velocity, na.rm = TRUE),
    Player_Max_Velocity = first(Player.Max.Velocity),
    .groups = "drop"
  ) %>%
  mutate(
    pct_of_max_velocity = (Weekly_Max_Velocity / Player_Max_Velocity) * 100
  )

# Create percentage of weekly efforts in each band
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  mutate(
    pct_V2 = (V2 / total_efforts) * 100,
    pct_V3 = (V3 / total_efforts) * 100,
    pct_V4 = (V4 / total_efforts) * 100,
    pct_V5 = (V5 / total_efforts) * 100,
    pct_V6 = (V6 / total_efforts) * 100,
    pct_V7 = (V7 / total_efforts) * 100,
    pct_V8 = (V8 / total_efforts) * 100
  )

weekly_velocity_efforts <- weekly_velocity_efforts %>%
  filter(total_efforts > 0)
# Plot for each player

# Loop over all players and display their plots
unique(weekly_velocity_efforts$anon_id) %>%
  lapply(function(player_id) {
    
    # Prepare the data for one player
    player_weekly_data <- weekly_velocity_efforts %>%
      filter(anon_id == player_id) %>%
      select(week, pct_V2:pct_V8, pct_of_max_velocity) %>%
      pivot_longer(
        cols = starts_with("pct_V"),
        names_to = "velocity_band",
        values_to = "percent_effort"
      ) %>%
      mutate(
        velocity_band = factor(
          velocity_band,
          levels = paste0("pct_V", 8:2),
          labels = paste0("V", 8:2)
        )
      )
    
    # Skip empty data
    if (nrow(player_weekly_data) == 0) return(NULL)
    
    # Generate and print the plot
    plot <- ggplot(player_weekly_data, aes(x = week)) +
      geom_col(aes(y = percent_effort, fill = velocity_band), position = "stack") +
      geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
      geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
      scale_fill_manual(
        values = c(
          "V2" = "darkgreen",
          "V3" = "green2",
          "V4" = "greenyellow",
          "V5" = "yellow",
          "V6" = "orange",
          "V7" = "tomato",
          "V8" = "firebrick"
  )
) +
    scale_y_continuous(
        name = "Band Distribution (%)",
        sec.axis = sec_axis(~ ., name = "Weekly Max Velocity (% of PR)")
      ) +
      labs(
        title = paste("Velocity Band Effort % and Speed Trend for Player", player_id),
        x = "Week",
        fill = "Velocity Band"
      ) +
      theme_classic()
    
    print(plot)  # Display plot
    
    return(NULL)
  })

Each player is different in reaching different velocity bands in relation to their top speeds. Some players, ID_11 for example, rarely enter bands 6 or higher, even if they are close to 100% of their max velocity. This shows the issues with using absolute bands for the whole team.

# Pick one player
player_id <- "ID_93"

# Filter data for that player and weeks
player_data <- weekly_velocity_efforts %>%
  filter(anon_id == player_id)

# Plot of Total Sprinting Efforts per Week
ggplot(player_data, aes(x = week, y = total_efforts)) +
  geom_line(color = "#CFB87C", size = 1) +
  geom_point(color = "#CFB87C", size = 2) +
  labs(
    title = paste("Total Sprinting Efforts per Week for Player", player_id),
    x = "Week",
    y = "Total Sprinting Efforts"
  ) +
  theme_classic()

#Plot of Sprint Effort Distribution by Velocity Band

# Reshape absolute counts to long format
player_counts_long <- player_data %>%
  select(week, V2, V3, V4, V5, V6, V7, V8) %>%
  pivot_longer(
    cols = V2:V8,
    names_to = "velocity_band",
    values_to = "effort_count"
  ) %>%
  mutate(
    velocity_band = factor(velocity_band, levels = paste0("V", 8:2))
  )

ggplot(player_counts_long, aes(x = week, y = effort_count, fill = velocity_band)) +
  geom_col(position = "stack") +
  scale_fill_brewer(palette = "Set2") +
  labs(
    title = paste("Sprint Effort Distribution by Velocity Band for Player", player_id),
    x = "Week",
    y = "Sprint Effort Count",
    fill = "Velocity Band"
  ) +
  theme_classic()

Exploring correlations between bands and max speeds



# Select relevant columns
cor_data <- weekly_velocity_efforts %>%
  select(pct_of_max_velocity, pct_V2, pct_V3, pct_V4, pct_V5, pct_V6, pct_V7, pct_V8)

# Compute correlation matrix
cor_matrix <- cor(cor_data, use = "pairwise.complete.obs")

cor_matrix

V4 and V5 appear to be slightly more predictive of top-speed achievement than V6, V7 or V8 — possibly because they’re more frequently reached zones.

V2 has a moderately strong negative linear relationship with reaching top-speed. When athletes spend more effort in this low-speed band, their weekly top speed (as % of max) tends to be lower.

# Linear model with all bands predicting pct_of_max_velocity
model_all_bands <- lm(
  pct_of_max_velocity ~ pct_V2 + pct_V3 + pct_V4 + pct_V5 + pct_V6 + pct_V7 + pct_V8,
  data = weekly_velocity_efforts
)
summary(model_all_bands)
# Compute change in top speeds week-to-week
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  arrange(anon_id, week) %>%
  group_by(anon_id) %>%
  mutate(
    pct_of_max_velocity_change = pct_of_max_velocity - lag(pct_of_max_velocity)
  ) %>%
  ungroup()
# Scatterplot with smoothing line for V8 band effort

# Create V8 data set that only includes when someone was in V8 for a given week
V8 <- weekly_velocity_efforts %>%
  filter(pct_V8 > 0)

# Plot
ggplot(V8, aes(x = pct_V8, y = pct_of_max_velocity_change)) +
  geom_point(alpha = 0.4) +
  geom_smooth(method = "loess", se = TRUE, color = "darkred") +
  labs(
    title = "Change in Max Speed vs. V8 Band Usage",
    x = "Percent of Total Effort in V8 Band (%)",
    y = "Change in Max Speed from Prior Week (%)"
  ) +
  theme_classic()
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  group_by(anon_id) %>%
  mutate(
    lag_V2 = lag(V2),
    lag_V3 = lag(V3),
    lag_V4 = lag(V4),
    lag_V5 = lag(V5),
    lag_V6 = lag(V6),
    lag_V7 = lag(V7),
    lag_V8 = lag(V8),
    
    lag_pct_V2 = lag(pct_V2),
    lag_pct_V3 = lag(pct_V3),
    lag_pct_V4 = lag(pct_V4),
    lag_pct_V5 = lag(pct_V5),
    lag_pct_V6 = lag(pct_V6),
    lag_pct_V7 = lag(pct_V7),
    lag_pct_V8 = lag(pct_V8)
  ) %>%
  ungroup()
# Maybe group by position

# Get Player positions
player_positions <- Catapult_Session_clean %>%
  select(anon_id, Primary.Position) %>%
  distinct()

# Join Position into weekly_velocity_efforts
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  left_join(player_positions, by = "anon_id")

# Position groups
weekly_velocity_efforts <- weekly_velocity_efforts %>%
  mutate(
    Position_Group = case_when(
      Primary.Position %in% c("QB", "LB", "TE", "RB") ~ "COMBO",
      Primary.Position %in% c("OL", "DL", "DE") ~ "BIGS",
      Primary.Position %in% c("WR", "DB", "DB, WR") ~ "SKILL",
      TRUE ~ "OTHER"
    )
  ) %>%
  filter(Position_Group != "OTHER")

ggplot(weekly_velocity_efforts, aes(x = Position_Group, y = pct_of_max_velocity_change)) +
  geom_boxplot(fill = "skyblue", outlier.color = "red", outlier.size = 1.5) +
  labs(
    title = "Weekly Change in % of Max Velocity by Position",
    x = "Position",
    y = "% Change in Max Velocity"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
weekly_band_effort_by_group <- weekly_velocity_efforts %>%
  group_by(week, Position_Group) %>%
  summarise(
    mean_V2 = mean(V2, na.rm = TRUE),
    mean_V3 = mean(V3, na.rm = TRUE),
    mean_V4 = mean(V4, na.rm = TRUE),
    mean_V5 = mean(V5, na.rm = TRUE),
    mean_V6 = mean(V6, na.rm = TRUE),
    mean_V7 = mean(V7, na.rm = TRUE),
    mean_V8 = mean(V8, na.rm = TRUE),
    
    mean_lag_V2 = mean(lag_V2, na.rm = TRUE),
    mean_lag_V3 = mean(lag_V3, na.rm = TRUE),
    mean_lag_V4 = mean(lag_V4, na.rm = TRUE),
    mean_lag_V5 = mean(lag_V5, na.rm = TRUE),
    mean_lag_V6 = mean(lag_V6, na.rm = TRUE),
    mean_lag_V7 = mean(lag_V7, na.rm = TRUE),
    mean_lag_V8 = mean(lag_V8, na.rm = TRUE),
    
    mean_pct_V2 = mean(pct_V2, na.rm = TRUE),
    mean_pct_V3 = mean(pct_V3, na.rm = TRUE),
    mean_pct_V4 = mean(pct_V4, na.rm = TRUE),
    mean_pct_V5 = mean(pct_V5, na.rm = TRUE),
    mean_pct_V6 = mean(pct_V6, na.rm = TRUE),
    mean_pct_V7 = mean(pct_V7, na.rm = TRUE),
    mean_pct_V8 = mean(pct_V8, na.rm = TRUE),
    mean_weekly_max_velocity = mean(Weekly_Max_Velocity, na.rm = TRUE),
    mean_pct_max_velocity = mean(pct_of_max_velocity, na.rm = TRUE),
    mean_pct_max_velocity_change = mean(pct_of_max_velocity_change, na.rm = TRUE),
    n_players = n()
  ) %>%
  ungroup()

# Pivot longer to get bands in one column for easier plotting
band_long <- weekly_band_effort_by_group %>%
  pivot_longer(
    cols = starts_with("mean_pct_V"),
    names_to = "velocity_band",
    values_to = "mean_pct_effort"
  ) %>%
  mutate(
    velocity_band = factor(velocity_band, levels = paste0("mean_pct_V", 2:8))
  )

ggplot(band_long, aes(x = week, y = mean_pct_effort, color = velocity_band)) +
  geom_line(size = 1) +
  facet_wrap(~ Position_Group) +
  labs(
    title = "Weekly Mean % Effort in Velocity Bands by Position Group",
    x = "Week",
    y = "Mean % Effort",
    color = "Velocity Band"
  ) +
   scale_color_manual(
        values = c(
          "mean_pct_V2" = "darkgreen",
          "mean_pct_V3" = "green2",
          "mean_pct_V4" = "greenyellow",
          "mean_pct_V5" = "yellow",
          "mean_pct_V6" = "orange",
          "mean_pct_V7" = "tomato",
          "mean_pct_V8" = "firebrick"
  )
) +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

This shows the mean percent of total efforts in each band over weeks by each position group.

# Plot mean weekly max velocity
ggplot(weekly_band_effort_by_group, aes(x = week, y = mean_weekly_max_velocity, color = Position_Group)) +
  geom_line(size = 1.2) +
  labs(
    title = "Mean Weekly Max Velocity by Position Group",
    x = "Week",
    y = "Mean Weekly Max Velocity"
  ) +
  theme_classic()


# Plot mean % max velocity change
ggplot(weekly_band_effort_by_group, aes(x = week, y = mean_pct_max_velocity_change, color = Position_Group)) +
  geom_line(size = 1.2) +
  labs(
    title = "Mean % Change in Max Velocity by Position Group",
    x = "Week",
    y = "Mean % Change in Max Velocity"
  ) +
  theme_classic()
ggplot() +
  # Plot the stacked lines for band effort %
  geom_line(data = band_long, aes(x = week, y = mean_pct_effort, color = velocity_band), size = 1) +
  geom_line(data = weekly_band_effort_by_group,
            aes(x = week, y = mean_weekly_max_velocity),
            color = "black", size = 1.2) +

  facet_wrap(~ Position_Group) +
  labs(
    title = "Weekly Mean % Effort in Velocity Bands by Position Group\nwith Mean Weekly Max Velocity",
    x = "Week",
    y = "Mean % Effort",
    color = "Velocity Band"
  ) +
  scale_color_manual(
        values = c(
          "mean_pct_V2" = "darkgreen",
          "mean_pct_V3" = "green2",
          "mean_pct_V4" = "greenyellow",
          "mean_pct_V5" = "yellow",
          "mean_pct_V6" = "orange",
          "mean_pct_V7" = "tomato",
          "mean_pct_V8" = "firebrick"
  )
) +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Define max effort and max velocity range (adjust as needed)
max_effort <- 67
max_velocity <- max(weekly_band_effort_by_group$mean_weekly_max_velocity, na.rm = TRUE)

ggplot() +
  geom_line(data = band_long, aes(x = week, y = mean_pct_effort, color = velocity_band), size = 1) +
  
  # Scale max_velocity to % effort scale to overlay on left axis
  geom_line(data = weekly_band_effort_by_group, 
            aes(x = week, y = (mean_weekly_max_velocity / max_velocity) * max_effort, color = "Max Velocity"),
            size = 1.2) +
  
  facet_wrap(~ Position_Group) +
  scale_y_continuous(
    name = "Mean % Effort",
    sec.axis = sec_axis(~ . * max_velocity / max_effort, name = "Mean Weekly Max Velocity (m/s)")  # adjust unit if needed
  ) +
  scale_color_manual(
    values = c(
      "V2" = "darkgreen",
      "V3" = "green2",
      "V4" = "greenyellow",
      "V5" = "yellow",
      "V6" = "orange",
      "V7" = "tomato",
      "V8" = "firebrick",
      "Max Velocity" = "black"
    )
  ) +
  labs(
    title = "Weekly Mean % Effort in Velocity Bands by Position Group\nwith Mean Weekly Max Velocity",
    x = "Week",
    color = "Velocity Band"
  ) +
  theme_classic() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Modeling

# Separate by position group
bigs <- weekly_band_effort_by_group %>%
  filter(Position_Group == "BIGS")

skill <- weekly_band_effort_by_group %>%
  filter(Position_Group == "SKILL")

combo <- weekly_band_effort_by_group %>%
  filter(Position_Group == "COMBO")

# Bigs Model
model_bigs <- lm(
  mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, data = bigs
  )
summary(model_bigs)

# Skill Model
model_skill <- lm(
  mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, data = skill
  )
summary(model_skill)

# Combo Model
model_combo <- lm(
  mean_weekly_max_velocity ~ mean_lag_V2 + mean_lag_V3 + mean_lag_V4 + mean_lag_V5 + mean_lag_V6 + mean_lag_V7 + mean_lag_V8, data = combo)
summary(model_combo)
# Group correlation:

# Select relevant variables
vars <- c("mean_weekly_max_velocity", "mean_lag_V2", "mean_lag_V3", 
          "mean_lag_V4", "mean_lag_V5", "mean_lag_V6", "mean_lag_V7", "mean_lag_V8")

# Correlation matrices
cor_bigs <- cor(bigs[, vars], use = "complete.obs")
cor_skill <- cor(skill[, vars], use = "complete.obs")
cor_combo <- cor(combo[, vars], use = "complete.obs")
round(cor_bigs, 2)
round(cor_skill, 2)
round(cor_combo, 2)
# Reduce for better modeling
bigs <- bigs %>%
  mutate(
    low_band = (mean_lag_V2 + mean_lag_V3) / 2,
    mid_band = (mean_lag_V4 + mean_lag_V5 + mean_lag_V6) / 3,
    high_band = (mean_lag_V7 + mean_lag_V8) / 2
  )
model_bigs_simple <- lm(mean_weekly_max_velocity ~ low_band + mid_band + high_band, data = bigs)
summary(model_bigs_simple)


# Skill Group
skill <- skill %>%
  mutate(
    low_band = (mean_lag_V2 + mean_lag_V3 + mean_lag_V4) / 3,
    mid_band = (mean_lag_V5 + mean_lag_V6) / 2,
    high_band = (mean_lag_V7 + mean_lag_V8) / 2
  )
model_skill_simple <- lm(mean_weekly_max_velocity ~ low_band + mid_band + high_band, data = skill)
summary(model_skill_simple)

# Combo group
combo <- combo %>%
  mutate(
    low_band = (mean_lag_V2 + mean_lag_V3) / 2,
    mid_band = (mean_lag_V4 + mean_lag_V5) / 2,
    high_band = (mean_lag_V6 + mean_lag_V7 + mean_lag_V8) / 3
  )
model_combo_simple <- lm(mean_weekly_max_velocity ~ low_band + mid_band + high_band, data = combo)
summary(model_combo_simple)

Bigs model: Statistically significant overall and explains around 33% of the variation of max velocity. The low band is significantly negative. Every unit increase in the low band results in a decrease in max velocity. The mid and high bands are not significant but the high band shows a positive trend. Based on this model, for bigs, more low-band effort may reduce weekly top speed, possibly indicating underexposure of high speeds. High-band efforts might help, but aren’t clearly impactful in this group.

Skill model: Almost significant. Low band has a negative effect, mid band has a positive effect, and high band is not significant. Based on this model, for skill players, more low-intensity exposure seems detrimental to max speed, while moderate band (V5–V6) exposure may enhance it.

Combo model: Model is not significant. The combo position group contains many different type of positions (QBs, RBs, TEs, LBs) with different movement and velocities. This variability may obscure relationships.

Across groups, low-band exposure is consistently negatively related to peak weekly speed — suggesting that too much low-intensity work may reduce the capacity to reach high speeds.

Are relative efforts and bands more advantageous than the absolute bands provided?

Look at ID_11’s plot from the last question. This player is an offensive lineman. Looking at the plot, he is mostly in band 2 and 3 while never reaching bands 7 or 8 and rarely reaching band 6. Despite this, this athlete’s weekly max velocity is always at least 75% of their all-time max velocity. Only looking at the absolute bands that are provided, we might come to the conclusion that ID_11 is not achieving high running speeds, however after looking at his max velocity efforts relative to his all-time max velocity, he is reaching high running speeds.

Create Relative Bands for ID_11

# Create Relative Bands for ID_11

# Prepare ID_11 dataset with relative bands
ID_11 <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>% 
  filter(anon_id == "ID_11", Maximum.Velocity != 0) %>%
  filter(!is.na(Maximum.Velocity)) %>%
  mutate(
    week = floor_date(Date, unit = "week"),
    pct_of_max = (Maximum.Velocity / Player.Max.Velocity) * 100,
    # Create Relative Bands
    relative_band = cut(
      pct_of_max,
      breaks = c(0, 40, 50, 60, 70, 80, 90, 100),
      labels = c("V2 (<40%)", "V3 (40-50%)","V4 (50-60%)" ,"V5 (60-70%)", "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"),
      right = TRUE, # Set to TRUE to include 100% in band
      include.lowest = TRUE  # Includes 0 in first band
    ),
    # Reverse factor levels so highest band is on top in plot
  relative_band = factor(
    relative_band,
    levels = rev(c("V2 (<40%)", "V3 (40-50%)","V4 (50-60%)" ,"V5 (60-70%)", "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"))
  )
  )

# Summarize counts per week and relative band
weekly_effort <- ID_11 %>%
  group_by(week, relative_band) %>%
  summarise(count = n(), .groups = "drop") %>%
  group_by(week) %>%
  mutate(pct_effort = (count / sum(count)) * 100) %>%
  ungroup()

# Pivot wider to get percentages per band as columns
weekly_effort_wide <- weekly_effort %>%
  select(week, relative_band, pct_effort) %>%
  pivot_wider(names_from = relative_band, values_from = pct_effort, values_fill = 0)

# Calculate weekly max velocity % of player max velocity
weekly_max_velocity <- ID_11 %>%
  group_by(week) %>%
  summarise(pct_of_max_velocity = max(pct_of_max), .groups = "drop")

# Join the max velocity % back to wide effort data
weekly_effort_wide <- weekly_effort_wide %>%
  left_join(weekly_max_velocity, by = "week")

# Pivot longer for stacked bar plotting
weekly_effort_long <- weekly_effort_wide %>%
  pivot_longer(
    cols = c("V2 (<40%)", "V3 (40-50%)","V4 (50-60%)" ,"V5 (60-70%)", "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"),
    names_to = "relative_band",
    values_to = "pct_effort"
  ) %>%
  mutate(
    relative_band = factor(
      relative_band,
      levels = rev(c("V2 (<40%)", "V3 (40-50%)","V4 (50-60%)" ,"V5 (60-70%)", "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"))
    )
  )
# Plot Relative Bands for ID_11
ggplot(weekly_effort_long, aes(x = week, y = pct_effort, fill = relative_band)) +
  geom_col(position = "stack") +
  geom_line(aes(y = pct_of_max_velocity, group = 1), 
            color = "black", size = 1.2) +
  geom_point(aes(y = pct_of_max_velocity), 
             color = "black", size = 2) +
  scale_fill_manual(
    values = c(
      "V2 (<40%)" = "darkgreen",
      "V3 (40-50%)" = "green2",
      "V4 (50-60%)" = "greenyellow",
      "V5 (60-70%)" = "yellow",
      "V6 (70-80%)" = "orange",
      "V7 (80-90%)" = "tomato",
      "V8 (90-100%)" = "darkred"
    )
  ) +
  labs(
    title = "Relative Velocity Band Effort % per Week, ID_11",
    x = "Week",
    y = "Percent of Weekly Efforts",
    fill = "Relative Velocity Band"
  ) + 
  scale_y_continuous(
    sec.axis = sec_axis(~ ., name = "Percent of Max Velocity")
  ) +
  theme_classic()

Comparing this with the absolute bands graph of ID_11, we can see that this is a much better representation of their running speeds.

Relative Bands for each athlete

# Function to create relative bands and plot these for each athlete

# Arrange data so that the plot will display in anon_id order
clean_anons <- Catapult_Session_clean %>%
  arrange(anon_id)

# Loop over all athletes and create their relative bands plots
unique(clean_anons$anon_id) %>%
  lapply(function(player_id) {
    # Prepare data for player
    player_data <- Catapult_Session_clean %>%
      filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
      filter(anon_id == player_id, Maximum.Velocity != 0, !is.na(Maximum.Velocity)) %>%
      mutate(
        week = floor_date(Date, unit = "week"),
        pct_of_max = (Maximum.Velocity / Player.Max.Velocity) * 100,
        # Create Relative Bands
        relative_band = cut(
          pct_of_max,
          breaks = c(0, 40, 50, 60, 70, 80, 90, 100),
          labels = c("V2 (<40%)", "V3 (40-50%)", "V4 (50-60%)", "V5 (60-70%)",
                     "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"),
          right = TRUE, # Set to true to include 100%
          include.lowest = TRUE  # Set to true to include 0 in 1st interval
        ),
        # Reverse factor levels so highest band is on top in plot
        relative_band = factor(
          relative_band,
          levels = rev(c("V2 (<40%)", "V3 (40-50%)", "V4 (50-60%)", "V5 (60-70%)",
                         "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"))
        )
      )
    
    if (nrow(player_data) == 0) return(NULL)  # skip empty

    # Summarize counts per week and relative band
    weekly_effort <- player_data %>%
      group_by(week, relative_band) %>%
      summarise(count = n(), .groups = "drop") %>%
      group_by(week) %>%
      mutate(pct_effort = (count / sum(count)) * 100) %>%
      ungroup()

    # Pivot wider to get percentages per band as columns
    weekly_effort_wide <- weekly_effort %>%
      select(week, relative_band, pct_effort) %>%
      pivot_wider(names_from = relative_band, values_from = pct_effort, values_fill = 0)

    # Calculate weekly max velocity % of player max velocity
    weekly_max_velocity <- player_data %>%
      group_by(week) %>%
      summarise(pct_of_max_velocity = max(pct_of_max), .groups = "drop")

    # Join
    weekly_effort_wide <- weekly_effort_wide %>%
      left_join(weekly_max_velocity, by = "week")
    
    # Pivot longer for stacked bar plotting
    weekly_effort_long <- weekly_effort_wide %>%
      pivot_longer(
          cols = -c(week, pct_of_max_velocity),
        names_to = "relative_band",
        values_to = "pct_effort"
      ) %>%
      mutate(
        relative_band = factor(
          relative_band,
          levels = rev(c("V2 (<40%)", "V3 (40-50%)", "V4 (50-60%)", "V5 (60-70%)",
                         "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)"))
        )
      )
    
    # Plot
    plot <- ggplot(weekly_effort_long, aes(x = week, y = pct_effort, fill = relative_band)) +
      geom_col(position = "stack") +
      geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
      geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
      scale_fill_manual(
        values = c(
          "V2 (<40%)" = "darkgreen",
          "V3 (40-50%)" = "green2",
          "V4 (50-60%)" = "greenyellow",
          "V5 (60-70%)" = "yellow",
          "V6 (70-80%)" = "orange",
          "V7 (80-90%)" = "tomato",
          "V8 (90-100%)" = "darkred"
        )
      ) +
      scale_y_continuous(
        name = "Percent of Weekly Effort",
        sec.axis = sec_axis(~ ., name = "Percent of Max Velocity")
      ) +
      labs(
        title = paste("Relative Velocity Band Effort % and Max Velocity Trend for Player", player_id),
        x = "Week",
        fill = "Relative Velocity Band"
      ) +
      theme_classic()

    print(plot)
    
    return(NULL)
  })
# Create relative bands for all athletes

# Define the velocity band labels
band_levels <- c("V2 (<40%)", "V3 (40-50%)", "V4 (50-60%)", "V5 (60-70%)",
                     "V6 (70-80%)", "V7 (80-90%)", "V8 (90-100%)")

# Create exposure data frame for all players
weekly_sprint_exposure <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
  filter(Maximum.Velocity != 0, !is.na(Maximum.Velocity)) %>%
  mutate(
    week = floor_date(Date, unit = "week"),
    pct_of_max = (Maximum.Velocity / Player.Max.Velocity) * 100,
    relative_band = cut(
      pct_of_max,
      breaks = c(0, 40, 50, 60, 70, 80, 90, 100.1),
      labels = band_levels,
      right = TRUE,  # will include 100
      include.lowest = TRUE
    ),
    relative_band = factor(relative_band, levels = band_levels)
  ) %>%
  group_by(anon_id, week, relative_band) %>%
  summarise(effort_count = n(), .groups = "drop") %>%
  group_by(anon_id, week) %>%
  mutate(
    total_efforts = sum(effort_count),
    pct_effort = (effort_count / total_efforts) * 100
  ) %>%
  ungroup() %>%
  select(anon_id, week, relative_band, pct_effort) %>%
  pivot_wider(
    names_from = relative_band,
    values_from = pct_effort,
    values_fill = 0
  )

# Add weekly max velocity % of max
weekly_max_pct <- Catapult_Session_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01")) %>%
  filter(Maximum.Velocity != 0, !is.na(Maximum.Velocity)) %>%
  mutate(
    week = floor_date(Date, unit = "week"),
    pct_of_max = (Maximum.Velocity / Player.Max.Velocity) * 100
  ) %>%
  group_by(anon_id, week) %>%
  summarise(pct_of_max_velocity = max(pct_of_max, na.rm = TRUE), .groups = "drop")

# Final data set
relative_bands <- weekly_sprint_exposure %>%
  left_join(weekly_max_pct, by = c("anon_id", "week"))

# Convert to long for plotting
relative_bands_long <- relative_bands %>%
  pivot_longer(
    cols = starts_with("V"),
    names_to = "relative_band",
    values_to = "pct_effort"
  ) %>%
  mutate(
    relative_band = factor(relative_band, levels = band_levels)
  )
COMBO <- c("QB","LB","TE","RB", "ILB")
BIG <- c("OL", "DL", "DE", "DT")
SKILL <- c("WR", "DB", "CB", "SAF")
Positions <- c("COMBO", "BIG", "SKILL")

weekly_sprint_exposure <- left_join(weekly_sprint_exposure,player_positions, by = "anon_id", relationship = "many-to-many")

weekly_sprint_exposure <- weekly_sprint_exposure %>%
  mutate(Position = case_when(Primary.Position %in% COMBO ~ "COMBO",
                               Primary.Position %in% BIG ~ "BIG",
                               Primary.Position %in% SKILL ~ "SKILL"))

weekly_sprint_exposure <- distinct(weekly_sprint_exposure)

max_velocities <- weekly_velocity_efforts[,c("anon_id","week","pct_of_max_velocity")]

weekly_sprint_exposure <- left_join(weekly_sprint_exposure, max_velocities, by = c("anon_id", "week"), relationship="many-to-many")




sprint_exposure_big <- weekly_sprint_exposure %>%
  filter(Position == "BIG") %>%
  group_by(week) %>%
  mutate(avg_pct_max = mean(pct_of_max_velocity),
         avg_V8 = mean(`V8 (90-100%)`),
         avg_V7 = mean(`V7 (80-90%)`),
         avg_V6 = mean(`V6 (70-80%)`),
         avg_V5 = mean(`V5 (60-70%)`),
         avg_V4 = mean(`V4 (50-60%)`),
         avg_V3 = mean(`V3 (40-50%)`),
         avg_V2 = mean(`V2 (<40%)`)) %>%
  ungroup() %>%
  na.omit()

sprint_exposure_combo <- weekly_sprint_exposure %>%
  filter(Position == "COMBO")  %>%
  group_by(week) %>%
  mutate(avg_pct_max = mean(pct_of_max_velocity),
         avg_V8 = mean(`V8 (90-100%)`),
         avg_V7 = mean(`V7 (80-90%)`),
         avg_V6 = mean(`V6 (70-80%)`),
         avg_V5 = mean(`V5 (60-70%)`),
         avg_V4 = mean(`V4 (50-60%)`),
         avg_V3 = mean(`V3 (40-50%)`),
         avg_V2 = mean(`V2 (<40%)`)) %>%
  ungroup() %>%
  na.omit()

sprint_exposure_skill <- weekly_sprint_exposure %>%
  filter(Position == "SKILL")  %>%
  group_by(week) %>%
  mutate(avg_pct_max = mean(pct_of_max_velocity),
         avg_V8 = mean(`V8 (90-100%)`),
         avg_V7 = mean(`V7 (80-90%)`),
         avg_V6 = mean(`V6 (70-80%)`),
         avg_V5 = mean(`V5 (60-70%)`),
         avg_V4 = mean(`V4 (50-60%)`),
         avg_V3 = mean(`V3 (40-50%)`),
         avg_V2 = mean(`V2 (<40%)`)) %>%
  ungroup() %>%
  na.omit()
  

When I built the models that considered the relative bands instead of the absolute bands provided, I found that initially, the results were a lot better than the previous models with the absolute bands. This suggests that the relative bounds which are segmented into 10% chunks of all time maximum velocity for each player is a lot more indicative of effort and therefore their percentage of maximum velocity in a given week. We are able to see though that there is a lot of multicollinearity within the relative bands calculated.

# Modeling

# Bigs Model
model_big <- lm(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
                data=sprint_exposure_big)
summary(model_big)


# Skill Model
model_skill <- lm(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
                data=sprint_exposure_skill)
summary(model_skill)


# Combo Model
model_combo <- lm(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
               data=sprint_exposure_combo)
summary(model_combo)

Looking at the correlations between all of the predictors as well as the response variable in the model we can see that a lot of the predictors have super strong correlations which each other, some more so than they are with the response. This suggests that the issue with the models above is that multicollinearity is bogging them down and we are not seeing the true relationships between the predictors and the response.

cor(sprint_exposure_big[,13:20])
cor(sprint_exposure_skill[,13:20])
cor(sprint_exposure_combo[,13:20])

All of the models below are the best 3 predictor models that resulted for each position after running the best subsets algorithm. All of the following have reductions in adjusted-\(R^2\) but they don’t seem significant considering that we took out over half of the predictors and maintained a relatively similar adjusted-\(R^2\) value.

#Bigs

#best_sub_big <- regsubsets(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
#                data=sprint_exposure_big, method="exhaustive")
#summary(best_sub_big)

model_big <- lm(avg_pct_max~avg_V2+avg_V6+avg_V7,
                data=sprint_exposure_big)
summary(model_big)


#Skills

#best_sub_skill <- regsubsets(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
#                data=sprint_exposure_skill, method="exhaustive")
#summary(best_sub_skill)

model_skill <- lm(avg_pct_max~avg_V2+avg_V5+avg_V6,
                data=sprint_exposure_skill)
summary(model_skill)

#Combos
#best_sub_combo <- regsubsets(avg_pct_max~avg_V2+avg_V3+avg_V4+avg_V5+avg_V6+avg_V7+avg_V8,
#                data=sprint_exposure_combo, method="exhaustive")
#summary(best_sub_combo)

model_combo <- lm(avg_pct_max~avg_V2+avg_V4+avg_V5,
               data=sprint_exposure_combo)
summary(model_combo)

Since the models perform roughly the same with just 3 predictors as they do with all 7, it may make sense to instead truncate into more general bins. This may help us understand the relationship with low, medium and high effort with weekly maximum velocity.

sprint_exposure_big <- sprint_exposure_big %>%
  mutate(avg_low = (avg_V2+avg_V3)/2,
         avg_medium = (avg_V4+avg_V5+avg_V6)/3,
         avg_high = (avg_V7+avg_V8)/2)

sprint_exposure_skill <- sprint_exposure_skill %>%
  mutate(avg_low = (avg_V2+avg_V3)/2,
         avg_medium = (avg_V4+avg_V5+avg_V6)/3,
         avg_high = (avg_V7+avg_V8)/2)

sprint_exposure_combo <- sprint_exposure_combo %>%
  mutate(avg_low = (avg_V2+avg_V3)/2,
         avg_medium = (avg_V4+avg_V5+avg_V6)/3,
         avg_high = (avg_V7+avg_V8)/2)
#bigs
model_big_general <- lm(avg_pct_max~avg_low+avg_medium+avg_high,
                        data=sprint_exposure_big)
summary(model_big_general)

#skills
model_skill_general <- lm(avg_pct_max~avg_low+avg_medium+avg_high,
                        data=sprint_exposure_skill)
summary(model_skill_general)

#combos
model_combo_general <- lm(avg_pct_max~avg_low+avg_medium+avg_high,
                        data=sprint_exposure_combo)
summary(model_combo_general)

Still pretty bad, I am going to use principle components regression to see if there are other relationships we are missing

preds <- sprint_exposure_big[,14:20]

pc_loadings <- prcomp(preds, scale=TRUE)$rotation[,c(1,2,3)]
pc_loadings

How does sprinting exposure (# of efforts, % max reached) relate to incidence of hamstring injuries?

# Join relative_bands data set with injury data

Incident_dates <- Incident_Report_clean %>%
  filter(Date >= as.Date("2024-06-30") & Date <= as.Date("2025-07-01"))

# Get ids of athletes with injury
injured_ids <- unique(Incident_dates$anon_id)

# Filter relative_bands to only include athletes who got injured
injured_data <- relative_bands_long %>%
  filter(anon_id %in% injured_ids)

# Create injury weeks
injury_weeks <- Incident_Report_clean %>%
  mutate(
    week = floor_date(as.Date(Date.of.Injury), unit = "week")
  ) %>%
  filter(Date.of.Injury >= as.Date("2024-06-30") & Date.of.Injury <= as.Date("2025-07-01")) %>%
  select(anon_id, week) %>%
  distinct() %>%
  mutate(injury = 1)

injuries_and_running <- injured_data %>%
  left_join(injury_weeks, by = c("anon_id", "week")) %>%
  mutate(injury = ifelse(is.na(injury), 0, injury))
# For demonstration: pick one player (or loop over them)
player_id <- "ID_307"

injuries_and_running <- injuries_and_running %>%
  mutate(relative_band = factor(relative_band, levels = band_levels))

plot_data <- injuries_and_running %>%
  filter(anon_id == player_id)

# Find injury weeks for this player
injury_week_nums <- plot_data %>% filter(injury == 1) %>% pull(week)

ggplot(plot_data, aes(x = week)) +
  # Highlight injury week(s) as shaded rectangles
  geom_rect(data = data.frame(week = injury_week_nums),
            aes(xmin = week - 3, xmax = week + 3, ymin = -Inf, ymax = Inf),
            fill = "blue", alpha = 0.2, inherit.aes = FALSE) +
  geom_col(aes(y = pct_effort, fill = relative_band), position = position_stack(reverse = TRUE)) +
  geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
  geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
  scale_fill_manual(
    values = c(
      "V2 (<40%)" = "darkgreen",
      "V3 (40-50%)" = "green2",
      "V4 (50-60%)" = "greenyellow",
      "V5 (60-70%)" = "yellow",
      "V6 (70-80%)" = "orange",
      "V7 (80-90%)" = "tomato",
      "V8 (90-100%)" = "darkred"
    )
  ) +
  labs(
    title = paste("Relative Velocity Band Effort % and Max Velocity Trend for", player_id),
    x = "Week",
    y = "Percent of Weekly Efforts",
    fill = "Relative Velocity Band"
  ) +
  scale_y_continuous(
    sec.axis = sec_axis(~ ., name = "Percent of Max Velocity")
  ) +
  theme_classic()
# Loop over injured athletes and create their plots

unique(injuries_and_running$anon_id) %>%
  lapply(function(player_id) {
    
    # Filter data for this player
    plot_data <- injuries_and_running %>%
      filter(anon_id == player_id)
    
    if (nrow(plot_data) == 0) return(NULL)  # Skip if no data
    
    # Get injury weeks
    injury_week_nums <- plot_data %>%
      filter(injury == 1) %>%
      pull(week)
    
    # Plot
    plot <- ggplot(plot_data, aes(x = week)) +
      # Highlight injury weeks with shaded blue areas
      geom_rect(data = data.frame(week = injury_week_nums),
                aes(xmin = week - 3, xmax = week + 3, ymin = -Inf, ymax = Inf),
                fill = "blue", alpha = 0.2, inherit.aes = FALSE) +
      geom_col(aes(y = pct_effort, fill = relative_band), position = position_stack(reverse = TRUE)) +
      geom_line(aes(y = pct_of_max_velocity, group = 1), color = "black", size = 1.2) +
      geom_point(aes(y = pct_of_max_velocity), color = "black", size = 2) +
      scale_fill_manual(
        values = c(
          "V2 (<40%)" = "darkgreen",
          "V3 (40-50%)" = "green2",
          "V4 (50-60%)" = "greenyellow",
          "V5 (60-70%)" = "yellow",
          "V6 (70-80%)" = "orange",
          "V7 (80-90%)" = "tomato",
          "V8 (90-100%)" = "darkred"
        )
      ) +
      scale_y_continuous(
        name = "Percent of Weekly Effort",
        sec.axis = sec_axis(~ ., name = "Percent of Max Velocity")
      ) +
      labs(
        title = paste("Velocity Band Exposure & Max % for Injured Player", player_id),
        x = "Week",
        fill = "Relative Velocity Band"
      ) +
      theme_classic()
    
    print(plot)
    
    return(NULL)
  })

The blue lines indicate a week where an injury occurred. No real insights can be made from looking at relative velocity bands and injury occurrances.

model_injury <- glm(injury ~ pct_of_max_velocity,
                    data = injuries_and_running,
                    family = "binomial")

summary(model_injury)

Simple model Each 1 percentage point increase in pct_of_max_velocity increases the log-odds of injury by 0.0106. Not significant.

# Get summary stats for both injury and non injury

# Clean and get date format
injuries_and_running_clean <- injuries_and_running %>%
  mutate(week_formatted = format(as.Date(week), "%m-%d-%Y")) %>%
  distinct(anon_id, week, .keep_all = TRUE)

# Filter for only injury weeks
injury_weeks <- injuries_and_running_clean %>%
  filter(injury == 1) %>%
  mutate(injury_event = paste0(anon_id, "__", week_formatted))

# Calculate statistics for injury weeks
injury_mean <- mean(injury_weeks$pct_of_max_velocity, na.rm = TRUE)

injury_summary_stats <- injury_weeks %>%
  summarise(
    mean_pct = mean(pct_of_max_velocity, na.rm = TRUE),
    sd_pct = sd(pct_of_max_velocity, na.rm = TRUE),
    n = sum(!is.na(pct_of_max_velocity))
  )
# Confidence interval 
se <- injury_summary_stats$sd_pct / sqrt(injury_summary_stats$n)
t_crit <- qt(0.975, df = injury_summary_stats$n - 1)
lower <- injury_summary_stats$mean_pct - t_crit * se
upper <- injury_summary_stats$mean_pct + t_crit * se
# Print
cat("95% CI for mean pct_of_max_velocity:", round(lower,2), "-", round(upper,2))


# Filter for non-injury weeks
non_injury_weeks <- injuries_and_running_clean %>%
  filter(injury == 0)

# Get statistics for non-injury weeks
non_injury_mean <- mean(non_injury_weeks$pct_of_max_velocity, na.rm = TRUE)

non_injury_summary_stats <- non_injury_weeks %>%
  summarise(
    mean_pct = mean(pct_of_max_velocity, na.rm = TRUE),
    sd_pct = sd(pct_of_max_velocity, na.rm = TRUE),
    n = sum(!is.na(pct_of_max_velocity))
  )

# Confidence interval
non_injury_se <- non_injury_summary_stats$sd_pct / sqrt(non_injury_summary_stats$n)
non_injury_t_crit <- qt(0.975, df = non_injury_summary_stats$n - 1)
non_injury_lower <- non_injury_summary_stats$mean_pct - non_injury_t_crit * non_injury_se
non_injury_upper <- non_injury_summary_stats$mean_pct + non_injury_t_crit * non_injury_se

# Print results
cat("95% CI for mean pct_of_max_velocity (non-injury weeks):", 
    round(non_injury_lower, 2), "-", round(non_injury_upper, 2))
# T-test to compare pct_of_max_velocity in injuries and non injuries that week
t_test <- t.test(
  pct_of_max_velocity ~ injury, 
  data = injuries_and_running_clean,
  var.equal = FALSE # use TRUE if you assume equal variances
)

# Print results
print(t_test)

Group 1 (injury) mean = 86.55 Group 2 (non-injury) mean = 85.49 While the mean % of max velocity is higher for the injury group, the difference is small and not statistcally significant. (high p-value, 0.6)

# Plot of % Max Velocity during injury and non injury weeks
ggplot(injuries_and_running_clean, aes(x = week, y = pct_of_max_velocity)) +
  geom_point(aes(color = factor(injury))) +
  scale_color_manual(values = c("0" = "black", "1" = "red"),
                     labels = c("No Injury", "Injury"),
                     name = "Injury Status") +
  labs(
    title = "% of Max Velocity by Week",
    subtitle = paste("Mean % of Max Velocity, Injury: ", round(injury_mean, 2), " |  Mean % of Max Velocity, Non-Injury: ", round(non_injury_mean, 2)),
    x = "Week",
    y = "% of Max Velocity Reached"
  ) +
  theme_classic()


ggplot(injury_weeks, aes(x = week, y = pct_of_max_velocity)) +
  geom_point(color = "red") +
  labs(
    title = "% of Max Velocity During Injury Weeks",
    subtitle = paste("% of Max Velocity Mean: ", round(injury_mean, 2)),
    x = "Week",
    y = "% of Max Velocity"
  ) +
  theme_classic()

# Create bar chart
ggplot(injury_weeks, aes(x = factor(injury_event), y = pct_of_max_velocity)) +
  geom_col(fill = "#CFB87C") +
  geom_text(aes(label = round(pct_of_max_velocity, 1)),  # round to 1 decimal place
            vjust = -0.5, size = 2.75) +
  labs(
    title = "% of Max Velocity During Injury Weeks",
    subtitle = paste("Mean: ", round(injury_mean, 2)),
    x = "Injury Event",
    y = "% of Max Velocity"
  ) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Looking into weeks before injury

# Calculate percent of max velocity from 1, 2, and 3 weeks prior as well as the sum of weeks 1 and 2 and the sum of weeks 1, 2, and 3 as well as the change between week one and week 2
week_prior <- injuries_and_running_clean %>%
  group_by(anon_id) %>%
  arrange(week) %>%
  mutate(
    # Previous weeks values of % of max velocity
    lag1_pct_of_max_velocity = lag(pct_of_max_velocity, 1),
    lag2_pct_of_max_velocity = lag(pct_of_max_velocity, 2),
    lag3_pct_of_max_velocity = lag(pct_of_max_velocity, 3),
    
    # Change of % of max from current week to previous weeks
    change_lastweek = pct_of_max_velocity - lag1_pct_of_max_velocity,
    change_last2weeks = pct_of_max_velocity - lag2_pct_of_max_velocity,
    change_last3weeks = pct_of_max_velocity - lag3_pct_of_max_velocity,
    
    # Sum % of max of current to previous weeks
    sum_lastweek = pct_of_max_velocity + lag1_pct_of_max_velocity,
    sum_last2weeks = pct_of_max_velocity + lag1_pct_of_max_velocity + lag2_pct_of_max_velocity,
    sum_last3weeks = pct_of_max_velocity + lag1_pct_of_max_velocity + lag2_pct_of_max_velocity + lag3_pct_of_max_velocity,
    
    # Change between weeks (not current week)
    change_weeks1_2 = lag1_pct_of_max_velocity - lag2_pct_of_max_velocity,
    change_weeks1_3 = lag1_pct_of_max_velocity - lag3_pct_of_max_velocity,
    
    # Sum of weeks (not current week)
    sum_weeks1_2 = lag1_pct_of_max_velocity + lag2_pct_of_max_velocity,
    sum_weeks1_2_3 = lag1_pct_of_max_velocity + lag2_pct_of_max_velocity + lag3_pct_of_max_velocity
    ) %>%
  ungroup()

# Get Injury Event variable and Injury dataset
injury_week_prior <- week_prior %>%
  filter(injury == 1) %>%
  mutate(injury_event = paste0(anon_id, "__", week_formatted))

# Get non-injury dataset
non_injury_week_prior <- week_prior %>%
  filter(injury == 0)

# Get Means
injury_week_prior_mean <- mean(injury_week_prior$lag1_pct_of_max_velocity)
non_injury_week_prior_mean <- mean(non_injury_week_prior$lag1_pct_of_max_velocity)
injury_week_prior_mean
non_injury_week_prior_mean



# Plot of % Max Velocity during injury and non injury weeks
ggplot(week_prior, aes(x = week, y = lag1_pct_of_max_velocity)) +
  geom_point(aes(color = factor(injury))) +
  scale_color_manual(values = c("0" = "black", "1" = "red"),
                     labels = c("No Injury", "Injury"),
                     name = "Injury Status") +
  labs(
    title = "% of Max Velocity of Prior Week",
    subtitle = paste("Mean % of Max Velocity, Injury: ", round(injury_week_prior_mean, 2), " |  Mean % of Max Velocity, Non-Injury: ", round(non_injury_week_prior_mean, 2)),
    x = "Week",
    y = "% of Max Velocity Reached"
  ) +
  theme_classic()


ggplot(injury_week_prior, aes(x = week, y = lag1_pct_of_max_velocity)) +
  geom_point(color = "red") +
  labs(
    title = "% of Max Velocity the week before injury",
    subtitle = paste("% of Max Velocity Mean: ", round(injury_week_prior_mean, 2)),
    x = "Week",
    y = "% of Max Velocity"
  ) +
  theme_classic()

# Bar chart for week prior to injury % Max
ggplot(injury_week_prior, aes(x = factor(injury_event), y = lag1_pct_of_max_velocity)) +
  geom_col(fill = "#CFB87C") +
  geom_text(aes(label = round(pct_of_max_velocity, 1)),  # round to 1 decimal place
            vjust = -0.5, size = 2.75) +
  labs(
    title = "% of Max Velocity the Week Before Injury",
    subtitle = paste("% Max Mean: ", round(injury_week_prior_mean, 2)),
    x = "Injury Event",
    y = "% of Max Velocity"
  ) +
  theme_classic() + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
# T-test to compare lag_pct_of_max_velocity (the week before) between injury and non injury
t_test_week_prior <- t.test(
  lag1_pct_of_max_velocity ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_week_prior)


# T-test to compare change between current week and last week
t_test_change_lastweek <- t.test(
  change_lastweek ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_change_lastweek)

# T-test to compare change between current week and 2 weeks ago
t_test_change_last2weeks <- t.test(
  change_last2weeks ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_change_last2weeks)

# T-test to compare change between current week and 3 weeks ago
t_test_change_last3weeks <- t.test(
  change_last3weeks ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_change_last3weeks)


# T-test to compare sum of current week, 1, 2, and 3 weeks before
t_test_sum_last3weeks <- t.test(
  sum_last3weeks ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_sum_last3weeks)

Mean % of max week before: injury = 86.6, non-injury = 85.9. Not sig (pvalue = 0.79)

Mean % change week before (current week % - last week %): injury = -0.057, non-injury = -0.49 Not sig (p-value = 0.89)

Mean % change of last 2 weeks (current - 2 weeks ago): injury = -2.68, non-injury = -0.65 Not sig, p-value = 0.44

Mean % change of last 3 weeks (current - 3 weeks ago): injury = -0.6, non-injury = -0.8. Not sig, p-value = 0.95

# Look into the last two weeks

# Drop NAs
lag_2_injury <- injury_week_prior %>%
  drop_na(lag2_pct_of_max_velocity)
lag_2_non_injury <- non_injury_week_prior %>%
  drop_na(lag2_pct_of_max_velocity)

# Calculate means
mean_lag2_injury <- mean(lag_2_injury$lag2_pct_of_max_velocity)
mean_lag2_non_injury <- mean(lag_2_non_injury$lag2_pct_of_max_velocity)
mean_lag2_injury
mean_lag2_non_injury

# Two weeks before:
# T-test to compare lag2_pct_of_max_velocity (2 weeks before) between injury and non injury
t_test_2week_prior <- t.test(
  lag2_pct_of_max_velocity ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_2week_prior)

# Change of last two weeks (lag1 - lag2)
t_test_change_weeks1_2 <- t.test(
  change_weeks1_2 ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_change_weeks1_2)

# Sum of weeks 1 and 2
t_test_sum_weeks1_2 <- t.test(
  sum_weeks1_2 ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_sum_weeks1_2)


# 3 weeks before
# T-test to compare lag3_pct_of_max_velocity (3 weeks before) between injury and non injury
t_test_3week_prior <- t.test(
  lag3_pct_of_max_velocity ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_3week_prior)

# Change between 1 weeks before and 3 weeks before
t_test_change_weeks1_3 <- t.test(
  change_weeks1_3 ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_change_weeks1_3)

# Sum of 1, 2, and 3 weeks before injury
t_test_sum_weeks1_2_3 <- t.test(
  sum_weeks1_2_3 ~ injury, 
  data = week_prior,
  var.equal = FALSE)
print(t_test_sum_weeks1_2_3)

2 weeks before injury: Means of % of max: injury = 88.7, non-injury = 85.8 Not a significant difference in means, p-value, 0.1833 > 0.05.

Means of change: injury = -2.34, non-injury = 0.108 Not significant (p-value = 0.47)

Mean of sum of weeks 1 and 2: injury = 175.14, non-injury = 171.72 Not significant, p-value = 0.333

3 weeks before injury: Means of % of max: injury = 86.7, non-injury = 85.9 Not a significant difference in means, high p-value, 0.7

Mean sum of 1, 2, and 3 weeks before injury: injury = 261.8, non-injury = 257.3 Not sig, p-value = 0.36

Mean change between 1 and 3 weeks before: injury = -0.26, non-injury = -0.27 Not sig, p-val = 0.99

model <- glm(injury ~ change_lastweek + change_last2weeks,
             data = week_prior, family = "binomial")
summary(model)

There is no clear evidence that % of max velocity in the current or prior weeks (or their changes/sums) differs between injury and non-injury weeks, based on t-tests of mean differences.

Comparing the number of >90% efforts with injury
# Convert 90 percent max to logical
# Instead of Yes/No it is True/False
catapult_weekly <- Catapult_Session_clean %>%
  mutate(
    Date = as.Date(Date),
    week = floor_date(Date, unit = "week"),
    Hit90 = Hit.90.Percent.Max == "Yes"  # Convert to logical
  ) %>%
  distinct(anon_id, Date, .keep_all = TRUE)


# Get all combinations of player and week
all_combinations <- catapult_weekly %>%
  distinct(anon_id, week)

# Count weekly >90% sprint hits
weekly_hits <- catapult_weekly %>%
  group_by(anon_id, week) %>%
  summarise(
    count_90pct = sum(Hit90, na.rm = TRUE),
    .groups = "drop"
  )

# Join with all combinations to include zero counts
weekly_90pct <- all_combinations %>%
  left_join(weekly_hits, by = c("anon_id", "week")) %>%
  mutate(count_90pct = replace_na(count_90pct, 0))

distrubution <- weekly_90pct %>%
  count(count_90pct) %>%
  arrange(desc(n))  # Optional: sort from most to least common
# Create injury weeks
injury_weeks <- Incident_Report_clean %>%
  mutate(
    week = floor_date(as.Date(Date.of.Injury), unit = "week")
  ) %>%
  filter(Date.of.Injury >= as.Date("2024-06-30") & Date.of.Injury <= as.Date("2025-07-01")) %>%
  select(anon_id, week) %>%
  distinct() %>%
  mutate(injury = 1)

# Merge with injury data to have column that indicates injury week
weekly_90pct_injuries <- weekly_90pct %>%
  left_join(injury_weeks, by = c("anon_id", "week")) %>%
  mutate(injury = replace_na(injury, 0))

# Calculate lagged >90% effort counts
weekly_90pct_injuries <- weekly_90pct_injuries %>%
  arrange(anon_id, week) %>%
  group_by(anon_id) %>%
  mutate(
    lag1_90pct_count = lag(count_90pct, 1),
    lag2_90pct_count = lag(count_90pct, 2),
    lag3_90pct_count = lag(count_90pct, 3),
    
    change_lastweek = count_90pct - lag1_90pct_count,
    change_last2weeks = count_90pct - lag2_90pct_count,
    change_last3weeks = count_90pct - lag3_90pct_count,
    
    sum_last2weeks = count_90pct + lag1_90pct_count,
    sum_last3weeks = count_90pct + lag1_90pct_count + lag2_90pct_count,
    
    avg_lastweek = (count_90pct + lag1_90pct_count) / 2,
    avg_last2weeks = (count_90pct + lag1_90pct_count + lag2_90pct_count) / 3,
    avg_last3weeks = (count_90pct + lag1_90pct_count + lag2_90pct_count + lag3_90pct_count) / 4
  ) %>%
  ungroup()
# Bar chart of sprint counts the week of an injury

# Only injuries
injury_summary <- weekly_90pct_injuries %>%
  group_by(count_90pct) %>%
  summarise(
    total_injuries = sum(injury),
    total_weeks = n()
  )

ggplot(injury_summary, aes(x = factor(count_90pct), y = total_injuries)) +
  geom_col(fill = "#CFB87C") +
  labs(
    x = "Weekly >90% Sprint Count",
    y = "Total Injuries",
    title = "Total Injury Weeks by Weekly Sprint Count"
  ) +
  theme_classic()
# Injury rate table for current week counts
injury_rate_table <- weekly_90pct_injuries %>%
  group_by(count_90pct) %>%                      # Group by sprint count
  summarise(
    total_weeks = n(),                           # Total weeks with this count
    injury_weeks = sum(injury == 1),             # How many had injury that week
    injury_rate = injury_weeks / total_weeks    # Proportion injured
  ) %>%
  arrange(desc(injury_rate))
injury_rate_table

# Table for counts over last few weeks
weekly_90pct_injuries <- weekly_90pct_injuries %>%
  mutate(avg2_group = round(avg_last2weeks, 1))

# Create binned groups: Low (0–1), Moderate (1–2), High (2+)
weekly_90pct_injuries <- weekly_90pct_injuries %>%
  mutate(load_group = case_when(
    avg_last2weeks < 1 ~ "Low (<1)",
    avg_last2weeks >= 1 & avg_last2weeks < 2 ~ "Moderate(1-2)",
    avg_last2weeks >= 2 ~ "High (2+)"
  ))

# Create a table of counts and injury rates by group
injury_rate_by_group <- weekly_90pct_injuries %>%
  group_by(load_group) %>%
  summarise(
    total_weeks = n(),
    injury_weeks = sum(injury == 1),
    injury_rate = injury_weeks / total_weeks
  ) %>%
  arrange(load_group)  # optional: order Low, Moderate, High
injury_rate_by_group

Current week counts: Injuries increase slightly from 0-3 counts with 3 having the highest injury rate of 1.163%. 4 and 5 counts have no injuries but a low sample size.

Counts over last few weeks (Grouped): High counts had no injuries Moderate counts had the highest injury rate (0.96%) Low counts had a injury rate of 0.65%

Injury rates increased from low counts (<1) injury rate of 0.65% to moderate counts (1-2) injury rate of 0.96% then dropped to 0% for high counts (2+). However, high counts have a small sample size (143), so the rate may be unstable.

# Create a contingency table manually
injury_table <- matrix(c(
  11, 1699,   # Low: injured, not injured
  6, 627,     # Moderate: injured, not injured
  0, 143      # High: injured, not injured
), nrow = 3, byrow = TRUE)

# Add row and column names for clarity
rownames(injury_table) <- c("Low", "Moderate", "High")
colnames(injury_table) <- c("Injured", "Not_Injured")

# Run Fisher's Exact Test
fisher_result <- fisher.test(injury_table)

# View the result
print(fisher_result)

Our p-value of 0.56 means that there is no statically significance association between our sprint load groups and injury. Injury events are pretty rare overall in our dataset, making it hard to detect subtle differences.

# T-tests:

# Current week
t.test(count_90pct ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)

# Week before
t.test(lag1_90pct_count ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)

# Change last week to this week
t.test(change_lastweek ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)

# Change last two weeks
t.test(change_last2weeks ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)

# Sum over last 2 weeks
t.test(sum_last2weeks ~ injury, data = weekly_90pct_injuries, var.equal = FALSE)
weekly_90pct_injuries <- weekly_90pct_injuries %>%
  mutate(
    sprint_exposure_bin = ifelse(count_90pct > 0, "Some", "None")
  )

table_exposure <- table(weekly_90pct_injuries$sprint_exposure_bin, weekly_90pct_injuries$injury)

# Fisher's Exact Test
fisher.test(table_exposure)
# Create top quartile exposure
cutoff <- quantile(weekly_90pct_injuries$count_90pct, 0.75, na.rm = TRUE)

weekly_90pct_injuries <- weekly_90pct_injuries %>%
  mutate(
    sprint_exposure_group = ifelse(count_90pct >= cutoff, "High", "Low")
  )

table_quartile <- table(weekly_90pct_injuries$sprint_exposure_group, weekly_90pct_injuries$injury)

fisher.test(table_quartile)
glm(injury ~ count_90pct, data = weekly_90pct_injuries, family = "binomial")

glm(injury ~ lag1_90pct_count, data = weekly_90pct_injuries, family = "binomial")

change <- glm(injury ~ change_lastweek, data = weekly_90pct_injuries, family = "binomial")
summary(change)
glm(injury ~ change_last2weeks, data = weekly_90pct_injuries, family = "binomial")


sum <- glm(injury ~ sum_last3weeks, data = weekly_90pct_injuries, family = "binomial")
summary(sum)
multi_model <- glm(
  injury ~ count_90pct + sum_last2weeks,
  data = weekly_90pct_injuries,
  family = "binomial"
)

summary(multi_model)
ggplot(weekly_90pct_injuries, aes(x = change_lastweek, y = injury)) +
  geom_jitter(height = 0.05, alpha = 0.3, color = "blue") +  # Jitter to spread points vertically
  geom_smooth(method = "glm", method.args = list(family = "binomial"), se = TRUE, color = "red") +  # Logistic regression curve
  labs(
    title = "Probability of Injury vs Change in >90% Sprint Counts (Last Week)",
    x = "Change in >90% Sprint Counts (Current Week - Previous Week)",
    y = "Injury (0 = No, 1 = Yes)"
  ) +
  theme_minimal()

No t-tests or models revealed any insights.

Separate sprint counts into groups

# Create groups for number of sprint counts

summary(weekly_90pct_injuries$count_90pct)

weekly_90pct_injuries <- weekly_90pct_injuries %>%
  mutate(
    sprint_group = case_when(
      count_90pct == 0          ~ "Low",
      count_90pct >= 1 & count_90pct <= 7  ~ "Moderate",
      count_90pct >= 8          ~ "High",
      TRUE                      ~ NA_character_
    )
  )

# check counts
table(weekly_90pct_injuries$sprint_group, useNA = "ifany")
group_model <- glm(
  injury ~ sprint_group,
  data = weekly_90pct_injuries,
  family = "binomial"
)

summary(group_model)
weekly_90pct_injuries %>%
  group_by(sprint_group) %>%
  summarise(
    injury_rate = mean(injury, na.rm = TRUE),
    n = n()
  ) %>%
  ggplot(aes(x = sprint_group, y = injury_rate, fill = sprint_group)) +
  geom_col() +
  geom_text(aes(label = scales::percent(injury_rate, accuracy = 0.1)), vjust = -0.5) +
  labs(title = "Injury Rate by Sprint Group",
       x = "Sprint Group",
       y = "Injury Rate") +
  theme_minimal()
#removing extra data
remove(band_long, bigs, clean_anons, combo, cor_data, cor_matrix, daily_90_counts,
       full_grid, hit_90_counts, ID_11, Incident_dates, injured_data, injuries_and_running,
       injury_by_group, injury_weeks, max_velocities, model_all_bands, model_big,
       model_bigs, model_combo, model_skill, player_counts_long, player_data,
       player_positions, plot_data, plots_by_position, position_averages,
       position_averages_with_team, pre_injury_weeks, QBs, relative_bands,
       relative_bands_long, skill, sprint_counts, sprint_exposure_big,
       sprint_exposure_combo, sprint_exposure_skill, sprint_injury_table, V8, 
       weekly_90_counts, weekly_band_effort_by_group, weekly_effort, weekly_effort_long,
       weekly_effort_wide, weekly_max_pct, weekly_max_velocity, weekly_sprint_counts,
       weekly_sprint_counts_lagged, weekly_sprint_exposure, weekly_velocity_efforts)

#removing extra values and functions
remove(all_athletes, all_weeks, band_levels, BIG, COMBO, SKILL, injured_ids,
       injury_week_nums, overall_avg, player_id, positions, Positions, qb_avg, team_avg,
       plot_hit_90_by_position)

Section 2: Running Imbalance

What is the variation at the team level and at each individual athlete level?

Looking at team data as a whole, since January 1, 2024 there is absolute no deviance from 0. That means that since January 1, 2024, the team has had the same average running imbalance of 0. This makes sense given that the team is so large and that imbalances can go from -100% to 100%. This suggests that throughout this time, at no point was there a team sway to one side. There also weren’t any points since January 1, 2024 that the team had any large spikes in average absolute value of running imbalance. This suggests that at no point throughout the season were there larger spikes than normal in running imbalance. Each player tends to have a very unique trend in their running imbalance. Looking at how the team varies but also at how each player varies throughout the season, it’s hard to make out any pattern that’s applicable to most people. The variance of running imbalance varies greatly between each player. Instead we looked at the variances between players who were injured and those who were not. Based on three different bootstrapped findings, we can see that the variances between players who were injured and those who were not were statistically significant.

For the first bootstrap, we compared the variance of the pooled groups meaning that the variance in running imbalance for players who were injured and those who were not were compared. This resulted in a 90% confidence interval which suggested that the difference in variance between the two groups is between 0.30 and 3.45. This suggests that when looking at the variance of the two groups separately but all the players are pooled together, the variances will most likely be different by factor between 0.30 and 3.45 and the variance for the injured pool will be greater than that of the uninjured pool. For the second bootstrap, each player’s variance was taken individually. This unpooled approach was taken to see if an individual player’s variance in running imbalance could potentially be related to HSI risk. The bootstrap algorithm in this case took the averaged variances of the bootstrapped sample for each group and compared them. This bootstrap produced a 90% confidence interval for the difference in average variance between the two groups is 0.79 to 1.32. These results suggest that players who sustained a hamstring injury since January 1, 2024 had, on average, a greater variance in their running imbalance by about 1.06. This suggests that there is a relationship between variance in running imbalance and HSI risk. This found increase in variability will be used to address the following questions. For the third bootstrap, we wanted to see if there was a difference in the average mean absolute value in running imbalance between players who were and weren’t injured. The bootstrapping algorithm for this test calculated the average absolute distance value or each running imbalance measurement and found the average for each sample. This test found that at the 90% significance level, injured players had an average running imbalance absolute value between 0.06 and 0.32 greater than their uninjured counterparts. These values though, when we consider that the range of running imbalance goes from 0 to 100 is small and may be hard to detect when out in the field.

We also looked at the relationship between running imbalance and higher level position. From this analysis we found that there doesn’t seem to be a super strong relationship between the three categories and average running imbalance variance. The bootstrap revealed that there are potentially significant differences between those who are Bigs and Combos. But, those who are Skills weren’t able to differentiate themselves between the two groups. Along with this, we looked at the average absolute value in running imbalance for the three groups. This analysis told us that while Combos and Bigs tended to have the same average absolute value running imbalance, Skills had a significantly higher average absolute running imbalance value. We can see from the very last chart in this analysis that Skills make up the most of those with hamstring injuries followed closely by Combos and Bigs making up around half of the amount of Skills. This is interesting considering that the amounts of Bigs, Skills, and Combos within the Historical Running data set are all roughly the same.

Team Analysis

#team variation
Historical_Running_clean %>%
  summarize(Team_Variation = var(Running.Imbalance))

#individual player variation
Historical_Running_clean %>%
  group_by(anon_id) %>%
  summarize(Player_Variation = var(na.omit(Running.Imbalance))) %>%
  ungroup() 

#average variance in running imbalance across all players
Historical_Running_clean %>%
  group_by(anon_id) %>%
  mutate(Player.var = var(na.omit(Running.Imbalance))) %>%
  ungroup() %>%
  summarize(Average_Player_Variance = mean(na.omit(Player.var)))

#making variance and average absolute value for each date to see trends
Historical_Running_clean <- Historical_Running_clean %>%
  group_by(Date) %>%
  mutate(Date.Variance = var(na.omit(Running.Imbalance)),
         Date.Avg.Abs.Value = mean(abs(na.omit(Running.Imbalance)))) %>%
  ungroup()
#calculating mean and variance for team data
team_mean <- mean(Historical_Running_clean$Running.Imbalance)
team_sd <- sd(Historical_Running_clean$Running.Imbalance)

#making scatter plot of team running imbalance data throughout season
ggplot(Historical_Running_clean, aes(Date, Running.Imbalance)) +
  geom_point(alpha = 0.3) +
  geom_hline(yintercept = team_mean, color = "#CFB87C") +
  geom_hline(yintercept = team_mean + team_sd) +
  geom_hline(yintercept = team_mean - team_sd) +
  geom_hline(yintercept = team_mean + (2*team_sd), color = "#A2A4A3") +
  geom_hline(yintercept = team_mean - (2*team_sd), color = "#A2A4A3") +
  geom_smooth(method = "lm", se = TRUE, color = "#CFB87C") +
  labs(title = "Team Running Imbalance Since January 1, 2024", y="Running Imbalance (%)",
       subtitle = "\u03BC = 0.08623412, \u03C3^2 = 14.94215") +
  theme_minimal()

#making scatter plot of team running imbalance data throughout season
ggplot(Historical_Running_clean, aes(Date, Running.Imbalance)) +
  geom_point(alpha = 0.3) +
  geom_hline(yintercept = team_mean, color = "#CFB87C") +
  geom_smooth(method = "lm", se = TRUE, color = "#CFB87C") +
  geom_line(aes(x=Date, y=Date.Avg.Abs.Value), color = "#CFB87C") +
  geom_line(aes(x=Date, y=-Date.Avg.Abs.Value), color = "#CFB87C") +
  labs(title = "Team Running Imbalance Since January 1, 2024", y="Running Imbalance (%)",
       subtitle = "\u03BC = 0.08623412, \u03C3^2 = 14.94215") +
  theme_minimal()

#making histogram of team running imbalance data
ggplot(Historical_Running_clean, aes(Running.Imbalance)) +
  geom_histogram() +
  labs(title = "Team Running Imbalance Since January 1, 2024", x="Running Imbalance") +
  theme_minimal()

Injured Analysis

#making histogram for running imbalance of all injured athletes
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(fill = "#CFB87C", alpha = 0.75) +
  #adding in 95% confidence interval
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.025), color = "#CFB87C") +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.975), color = "#CFB87C") +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players with HSI since January 1, 2024") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#making histogram for running imbalance of all uninjured athletes
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(fill = "black", alpha = 0.75) +
  #adding in 95% confidence interval
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.025)) +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.975)) +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players without HSI since January 1, 2024") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#Plotting injured and uninjured histograms over top one another
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Running.Imbalance)) +
  geom_histogram(alpha = 0.75) +
  #adding in 95% CI for uninjured players
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.05)) +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,]$Running.Imbalance, 0.95)) +
  geom_histogram(data = Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Running.Imbalance), fill = "#CFB87C", alpha = 0.75) +
  #adding in 95% CI for injured players
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.05), color = "#CFB87C") +
  geom_vline(xintercept = quantile(Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,]$Running.Imbalance, 0.95), color = "#CFB87C") +
  xlim(-21,21) +
  labs(title = "Running Imbalance for Players with and without HSI since January 1, 2024") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 1 row containing non-finite outside the scale range (`stat_bin()`).

#making scatter plot of running imbalance data for injured players
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,], aes(Date, Running.Imbalance)) + 
  geom_point(alpha = 0.3) +
  ylim(-21,21) +
  labs(title = "Running Imbalance for Players with HSI since January 1, 2024") +
  theme_minimal()
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_point()`).

#making scatter plot of running imbalance for uninjured players
ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,], aes(Date, Running.Imbalance)) + 
  geom_point(alpha = 0.3) +
  ylim(-21,21) +
  labs(title = "Running Imbalance for Players without HSI since January 1, 2024") +
  theme_minimal()
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_point()`).

Bootstrapping Differences between Injured and Uninjured Athletes

#Splitting up the data sets and calculating player variance and measurement absolute value
injured_data <- Historical_Running_clean[Historical_Running_clean$anon_id %in% injured_IDs,] %>%
  mutate(Player.Absolute.Dist = abs(Running.Imbalance)) %>%
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()

uninjured_data <- Historical_Running_clean[Historical_Running_clean$anon_id %in% uninjured_IDs,] %>%
  mutate(Player.Absolute.Dist = abs(Running.Imbalance)) %>%
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()
#making a data frame to hold all of the within group variances
group_variances <- data.frame(injured_var = rep(NA,5000),
                uninjured_var = rep(NA,5000),
                diff_in_var = rep(NA, 5000))

#bootstrap for variance, 5000 iterations
for(i in 1:5000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1672)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2391)
  
  #storing the calculated variances in data frame
  group_variances[i,1] = var(injured_sample$Running.Imbalance)
  group_variances[i,2] = var(uninjured_sample$Running.Imbalance)
  group_variances[i,3] = group_variances[i,1] - group_variances[i,2]
}
ggplot(data=group_variances, aes(diff_in_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_variances$diff_in_var, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(group_variances$diff_in_var, 0.95), color= "#CFB87C") +
  labs(x="Difference in Variance") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=group_variances) +
  geom_histogram(aes(injured_var), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_var), alpha = 0.75) +
  labs(x="Variance") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#removing junk that came from the loops
remove(i,team_mean, team_sd, injured_sample, uninjured_sample, group_variances, injured_data, uninjured_data, mean_player_variances, group_distance)
ggplot(data = mean_player_variances, aes(diff_in_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(mean_player_variances$diff_in_var, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(mean_player_variances$diff_in_var, 0.95), color= "#CFB87C") +
  labs(title = "Difference in Estimated Average Variance for Injured and Uninjured Athletes", x="Difference in Average Variance") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=mean_player_variances) +
  geom_histogram(aes(injured_var), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_var), alpha = 0.75) +
  labs(title = "Estimated Average Variance for Injured and Uninjured Athletes", x="Average Variance") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#making a data frame to hold all of the average absolute differences from 0
group_distance <- data.frame(injured_dist = rep(NA,5000),
                uninjured_dist = rep(NA,5000),
                diff_in_dist = rep(NA, 5000))

#bootstrap for variances, 5000 iterations
for(i in 1:5000){
  #random seed
  set.seed(i) 
  
  #taking samples from each of the data sets, same number of rows, replacement true
  injured_sample <- sample_n(injured_data, replace = TRUE, size = 1658)
  uninjured_sample <- sample_n(uninjured_data, replace=TRUE, size = 2405)
  
  #storing the calculated variances in data frame
  group_distance[i,1] = mean(injured_sample$Player.Absolute.Dist)
  group_distance[i,2] = mean(uninjured_sample$Player.Absolute.Dist)
  group_distance[i,3] = group_distance[i,1] - group_distance[i,2]
}
ggplot(data = group_distance, aes(diff_in_dist)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_distance$diff_in_dist, 0.05), color= "#CFB87C") +
  geom_vline(xintercept = quantile(group_distance$diff_in_dist, 0.95), color= "#CFB87C") +
  labs(x="Difference in Distance")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data = group_distance) +
  geom_histogram(aes(injured_dist), alpha = 0.75, fill ="#CFB87C") +
  geom_histogram(aes(uninjured_dist), alpha = 0.75) +
  labs(x="Average Absolute Value")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#removing junk that came from the loops
remove(i,team_mean, team_sd, injured_sample, uninjured_sample, group_variances, injured_data, uninjured_data, mean_player_variances, group_distance)

Position Analysis

#making lists to sort position into larger categories
COMBO <- c("QB","LB","TE","RB", "ILB")
BIG <- c("OL", "DL", "DE", "DT")
SKILL <- c("WR", "DB", "CB", "SAF")
Positions <- c("COMBO", "BIG", "SKILL")

#giving positions to incident report
Incident_Report_clean <- Incident_Report_clean %>%
  mutate(Specific.Position = Position,
         Position = case_when(Specific.Position %in% COMBO ~ "COMBO",
                               Specific.Position %in% BIG ~ "BIG",
                               Specific.Position %in% SKILL ~ "SKILL"))

#giving positions to catapult session
Catapult_Session_clean <- Catapult_Session_clean %>%
  mutate(Specific.Position = Primary.Position,
         Position = case_when(Primary.Position %in% COMBO ~ "COMBO",
                               Primary.Position %in% BIG ~ "BIG",
                               Primary.Position %in% SKILL ~ "SKILL"))

#only taking IDs and position names and categories
incident_info <- Incident_Report_clean[,c("anon_id", "Position", "Specific.Position")]
catapult_info <- Catapult_Session_clean[,c("anon_id", "Position", "Specific.Position")]

#comprehensive list of IDs, their position, and category
info <- distinct(rbind(incident_info, catapult_info))

#add this only historical running
Historical_Running_clean <- left_join(Historical_Running_clean, info, by="anon_id",
                                      relationship="many-to-many") %>%
  #calculating each player's variance in running imbalance
  group_by(anon_id) %>%
  mutate(Player.Variance = var(Running.Imbalance)) %>%
  ungroup()
for(i in 1:3){
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$Position==Positions[i],],aes(Running.Imbalance)) +
    geom_histogram() +
    labs(subtitle=Positions[i])
  
  print(p)
}
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 154 rows containing non-finite outside the scale range (`stat_bin()`).
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 154 rows containing non-finite outside the scale range (`stat_bin()`).
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 154 rows containing non-finite outside the scale range (`stat_bin()`).

Player_Summary_Stats <- distinct(Historical_Running_clean[,c("anon_id", "Position",
                                                 "Specific.Position", 
                                                 "Player.Variance")])
for(i in 1:3){
  p <- ggplot(data=Player_Summary_Stats[Player_Summary_Stats$Position==Positions[i],],
              aes(Player.Variance)) +
    geom_histogram() +
    labs(subtitle=Positions[i])
  
  print(p)
}
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 6 rows containing non-finite outside the scale range (`stat_bin()`).
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 3 rows containing non-finite outside the scale range (`stat_bin()`).
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 4 rows containing non-finite outside the scale range (`stat_bin()`).

Bootstrapping Different Positions

#splitting up data set into the different categories
COMBOS <- Player_Summary_Stats %>%
  filter(Position == "COMBO") %>%
  na.omit()

SKILLS <- Player_Summary_Stats %>%
  filter(Position == "SKILL") %>%
  na.omit()

BIGS <- Player_Summary_Stats %>%
  filter(Position == "BIG") %>%
  na.omit()

group_avg_variance <- data.frame(COMBO_var = rep(NA, 5000),
                                 SKILL_var = rep(NA, 5000),
                                 BIG_var = rep(NA, 5000))
Historical_Running_clean <- Historical_Running_clean %>%
  mutate(Abs.Value.Running.Imbalance = abs(Running.Imbalance))

COMBOS <- Historical_Running_clean %>%
  filter(Position == "COMBO")

SKILLS <- Historical_Running_clean %>%
  filter(Position == "SKILL")

BIGS <- Historical_Running_clean %>%
  filter(Position == "BIG")

group_avg_dist <- data.frame(COMBO_dist = rep(NA, 5000),
                             SKILL_dist = rep(NA, 5000),
                             BIG_dist = rep(NA, 5000))
ggplot(data=group_avg_variance, aes(COMBO_var)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(group_avg_variance$COMBO_var, 0.05)) +
  geom_vline(xintercept = quantile(group_avg_variance$COMBO_var, 0.95))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=group_avg_variance, aes(SKILL_var)) +
  geom_histogram(fill="#CFB87C") +
  geom_vline(xintercept = quantile(group_avg_variance$SKILL_var, 0.05), color="#CFB87C") +
  geom_vline(xintercept = quantile(group_avg_variance$SKILL_var, 0.95), color="#CFB87C")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=group_avg_variance, aes(BIG_var)) +
  geom_histogram(fill="#A2A4A3") +
  geom_vline(xintercept = quantile(group_avg_variance$BIG_var, 0.05), color="#A2A4A3") +
  geom_vline(xintercept = quantile(group_avg_variance$BIG_var, 0.95), color="#A2A4A3")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=group_avg_variance) +
  geom_histogram(aes(COMBO_var), alpha=0.5, fill="black") +
  geom_histogram(aes(SKILL_var), alpha=0.5, fill="#CFB87C") +
  geom_histogram(aes(BIG_var), alpha=0.5, fill="#A2A4A3")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Historical_Running_clean <- Historical_Running_clean %>%
  mutate(Abs.Value.Running.Imbalance = abs(Running.Imbalance))

COMBOS <- Historical_Running_clean %>%
  filter(Position == "COMBO")

SKILLS <- Historical_Running_clean %>%
  filter(Position == "SKILL")

BIGS <- Historical_Running_clean %>%
  filter(Position == "BIG")

group_avg_dist <- data.frame(COMBO_dist = rep(NA, 5000),
                             SKILL_dist = rep(NA, 5000),
                             BIG_dist = rep(NA, 5000))
for(i in 1:5000){
  set.seed(i)
  combo_sample <- sample_n(COMBOS, size=1203, replace=TRUE)
  skill_sample <- sample_n(SKILLS, size=1635, replace=TRUE)
  big_sample <- sample_n(BIGS, size=1224, replace=TRUE)
  
  group_avg_dist[i,1] <- mean(na.omit(combo_sample$Abs.Value.Running.Imbalance))
  group_avg_dist[i,2] <- mean(na.omit(skill_sample$Abs.Value.Running.Imbalance))
  group_avg_dist[i,3] <- mean(na.omit(big_sample$Abs.Value.Running.Imbalance))
}
ggplot(data=group_avg_dist) +
  geom_histogram(aes(COMBO_dist), alpha=0.5, fill="black") +
  geom_histogram(aes(SKILL_dist), alpha=0.5, fill="#CFB87C") +
  geom_histogram(aes(BIG_dist), alpha=0.5, fill="#A2A4A3") +
  labs(title = "Estimated Average Absolute Value RUnning Imbalance", x="Average Absolute Value Running Imbalance") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Injury_Incidents <- distinct(Incident_Report_clean[,c("anon_id", "Position", "Date.of.Injury")])

Position_counts <- na.omit(distinct(Historical_Running_clean[,c("anon_id", "Position")]))

ggplot(Injury_Incidents, aes(Position, fill=Position)) +
  geom_bar() +
  scale_fill_manual(values = c("COMBO" = "black", "SKILL" = "#CFB87C", "BIG" = "#A2A4A3")) +
  labs(title="Hamstring Injuries by Position") +
  theme_minimal()


ggplot(Position_counts, aes(Position, fill=Position)) +
  geom_bar() +
  scale_fill_manual(values = c("COMBO" = "black", "SKILL" = "#CFB87C", "BIG" = "#A2A4A3")) +
  labs(title="Total Position Counts on Team") +
  theme_minimal()

remove(big_sample, BIGS, catapult_info, combo_sample, COMBOS, confints, group_avg_variance,
       incident_info, info, p, Player_Summary_Stats, skill_sample, SKILLS, BIG, COMBO, i,
       Positions, SKILL, dist_confints, group_avg_variance, group_avg_dist, group_distance, 
       group_variances, injured_data, injured_sample, Injury_Incidents, mean_player_variances, 
       Position_counts, var_confints)

What is a meaningful change? What red flags should go off when we see a week-to-week change in running imbalance?

Based on the analysis below, there doesn’t seem to be any major discernible differences in running imbalance before or following an injury. This suggests that there may not be a direct link between HSI risk and running imbalance value directly beforehand. Instead, the trends of many players who were injured seems to have a relatively consistent trend not entirely dependent on time. Looking at summary statistics of running imbalance in the weeks leading up to and following a hamstring injury, there are also no glaring trends. For this analysis, we looked at the mean and variance in running imbalance per week leading up to and after an injury for all of the injured players with running imbalance data. This showed us that there is no clear indicator of HSI risk in running imbalance or any summary statistic of it. Instead it may be more useful to look at each player’s total running imbalance and their individual variance. This seems to be more of a useful tool for differentiating between injured and uninjured athletes.

#getting running imbalances for just injured players
Injured_Historical_Running <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs)

#making new column to represent when in time injury would be, negative means before injury and positive means after injury, 0 means date of injury if there's data for that day
Injured_Historical_Running$Weeks.After.Injury <- rep(NA, 1658)
Injured_Historical_Running$Injury.Count <- rep(NA, 1658)

#making new column in incident report for the injury count
Incident_Report_clean$Injury.Count <- rep(NA, 122)

#go through all of the injured players in the data set
for(i in 1:22){
  #get the dates each player was injured
  injury_dates <- unique(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)
  
  #go through all of the dates in which the player had an injury
  for(j in 1:length(injury_dates)){
    
    #calculate dates for 1, 2, 3, 4 weeks before and after each injury date
    past_1 <- injury_dates[j]-7
    past_2 <- injury_dates[j]-14
    past_3 <- injury_dates[j]-21
    past_4 <- injury_dates[j]-28
    future_1 <- injury_dates[j]+7
    future_2 <- injury_dates[j]+14
    future_3 <- injury_dates[j]+21
    future_4 <- injury_dates[j]+28
    
    #Calculating how many injuries this is for the player
    injury_count <- as.character((length(injury_dates)) - j + 1)
    
    #compare date of data point for each player to date of injury, store in Weeks.After.Injury column, store injury count
    
    #first week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > injury_dates[j] & 
                                 Injured_Historical_Running$Date<=future_1,]$Weeks.After.Injury <- "1"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > injury_dates[j] & 
                                 Injured_Historical_Running$Date<=future_1,]$Injury.Count <- injury_count
    
    #second week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_1 & 
                                 Injured_Historical_Running$Date<=future_2,]$Weeks.After.Injury <- "2"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_1 & 
                                 Injured_Historical_Running$Date<=future_2,]$Injury.Count <- injury_count
    
    #third week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_2 & 
                                 Injured_Historical_Running$Date<=future_3,]$Weeks.After.Injury <- "3"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_2 & 
                                 Injured_Historical_Running$Date<=future_3,]$Injury.Count <- injury_count
    
    #fourth week after injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_3 & 
                                 Injured_Historical_Running$Date<=future_4,]$Weeks.After.Injury <- "4"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date > future_3 & 
                                 Injured_Historical_Running$Date<=future_4,]$Injury.Count <- injury_count    
    #week right before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < injury_dates[j] & 
                                 Injured_Historical_Running$Date>=past_1,]$Weeks.After.Injury <- "-1"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < injury_dates[j] & 
                                 Injured_Historical_Running$Date>=past_1,]$Injury.Count <- injury_count
    #two weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_1 & 
                                 Injured_Historical_Running$Date>=past_2,]$Weeks.After.Injury <- "-2"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_1 & 
                                 Injured_Historical_Running$Date>=past_2,]$Injury.Count <- injury_count
    
    #three weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_2 & 
                                 Injured_Historical_Running$Date>=past_3,]$Weeks.After.Injury <- "-3"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_2 & 
                                 Injured_Historical_Running$Date>=past_3,]$Injury.Count <- injury_count
        
    #four weeks before injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_3 & 
                                 Injured_Historical_Running$Date>=past_4,]$Weeks.After.Injury <- "-4"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date < past_3 & 
                                 Injured_Historical_Running$Date>=past_4,]$Injury.Count <- injury_count
    
    #Date of Injury
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date == injury_dates[j],]$Weeks.After.Injury <- "0"
    
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] &
                                 Injured_Historical_Running$Date == injury_dates[j],]$Injury.Count <- injury_count
    
    
    #adding injury count to indicent report
    Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i] & Incident_Report_clean$Date.of.Injury==injury_dates[j],]$Injury.Count <- injury_count
    
  }
}


#making weeks after injury and injury count into a factor and combining data sets back together
Injured_Historical_Running <- Injured_Historical_Running %>%
  mutate(Weeks.After.Injury = factor(Weeks.After.Injury),
         Injury.Count = factor(Injury.Count))

Historical_Running_clean <- left_join(Historical_Running_clean, Injured_Historical_Running)

#getting rid of junk that was from the loop
remove(future_1, future_2, future_3, future_4, i, injury_count, injury_dates, j, past_1, past_2, past_3, past_4)
remove(Injured_Historical_Running)

Injury Risk

for(i in 1:22){
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id==injured_IDs[i],], aes(Date, Running.Imbalance)) +
    geom_line(linetype=1) +
    geom_point(aes(color=Weeks.After.Injury)) +
    geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury, linetype=2) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue")) +
    theme_minimal() +
    labs(title="Running Imbalance", subtitle = injured_IDs[i])
  
  print(p)
}

#making summary statistics of running imbalance per week relative to injury
Historical_Running_clean <- Historical_Running_clean %>%
  group_by(anon_id, Injury.Count, Weeks.After.Injury) %>%
  mutate(Weeks.After.Injury.Variability = var(Running.Imbalance),
         Weeks.After.Injury.Mean = mean(abs(Running.Imbalance))) %>%
  ungroup()
for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Weeks.After.Injury.Mean, group=Injury.Count)) +
  geom_line() +
  geom_point(aes(color=Weeks.After.Injury)) +
    xlim(min(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)-30, max(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)+30) +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title="Average Absolute Distance per Week",injured_IDs[i]) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue"))
  
  print(p)
}

for(i in 1:22){
  #looking at variance in running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Weeks.After.Injury.Variability, group=Injury.Count)) +
  geom_line() +
  geom_point(aes(color=Weeks.After.Injury)) +
    xlim(min(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)-30, max(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)+30) +
  geom_vline(xintercept = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury) +
    labs(title="Variance in Running Imbalance per Week", subtitle=injured_IDs[i]) +
    scale_color_manual(values = c("-4"="green", "-3"="yellow", "-2"="orange", "-1"="red", "0"="black","1"= "purple", "2"="navy", "3"="blue", "4"="skyblue"))
  
  print(p)
}
Warning: Removed 18 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 20 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 93 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 97 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 71 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 73 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 18 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 17 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 19 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 16 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 25 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 30 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 31 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 52 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 55 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 100 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 101 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 102 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 104 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 48 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 49 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 134 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 135 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 46 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 46 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 28 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 28 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 113 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 113 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 131 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 131 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 27 rows containing missing values or values outside the scale range
(`geom_point()`).

Detectable Trend with Injury Risk

trends <- data.frame(ID = all_IDs,
                     KC = rep(0, 71))
for(i in 1:71){
  #only plotting if there are over 15 data points for each player
  if(nrow(Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],])>15){
    #calculating how strong of a non linear trend there is using a gam
    df <- summary(gam(Running.Imbalance~s(Days.Since.Start),
              data=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],]))$edf
    #calculating how strong of a linear trend there is using kendall correlation
    Kendall_Cor <- cor(x=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],]$Days.Since.Start, y=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],]$Running.Imbalance, method="kendall")
    
    trends[trends$ID == all_IDs[i],2] = Kendall_Cor
  
    if(all_IDs[i] %in% injured_IDs){
      p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],],
             aes(Date, Running.Imbalance)) +
        geom_point(color="skyblue") +
        geom_line(color="skyblue") +
        labs(subtitle=Kendall_Cor)
    }
    if(!(all_IDs[i] %in% injured_IDs)){
      p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == all_IDs[i],],
             aes(Date, Running.Imbalance)) +
        geom_point() +
        geom_line() +
        labs(subtitle=Kendall_Cor)
    }
    
    print(p)
  }
}


#plotted only from January 1, 2025 to May 1, 2025
trends <- trends %>%
  mutate(injured = ifelse(ID %in% injured_IDs,1,0))

ggplot(trends[trends$KC>0 & trends$injured==1,], aes(KC)) +
  geom_histogram()

ggplot(trends[trends$KC>0 & trends$injured==0,], aes(KC)) +
  geom_histogram()
injured_trends <- trends %>%
  filter(injured==1)

uninjured_trends <- trends %>%
  filter(injured==0)


kendall_cors <- data.frame(injured_avg_KC = rep(NA,5000),
                           uninjured_avg_KC = rep(NA,5000),
                           avg_diff_KC = rep(NA,5000))

for(i in 1:5000){
  set.seed(i)
  injured_samp <- sample_n(injured_trends, size=22, replace=TRUE)
  uninjured_samp <- sample_n(uninjured_trends, size=49, replace=TRUE)
  
  kendall_cors[i,1] <- mean(abs(injured_samp$KC))
  kendall_cors[i,2] <- mean(abs(uninjured_samp$KC))
  kendall_cors[i,3] <- kendall_cors[i,1] - kendall_cors[i,2]
}
ggplot(data=kendall_cors, aes(avg_diff_KC)) +
  geom_histogram() +
  geom_vline(xintercept = quantile(kendall_cors$avg_diff_KC, 0.05), color = "#CFB87C") +
  geom_vline(xintercept = quantile(kendall_cors$avg_diff_KC, 0.95), color = "#CFB87C") +
  labs(title = "Estimated Difference in Average Kendall Rank Correlation Coefficient",
       x = "Difference in Average Kendall Coefficient")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ggplot(data=kendall_cors) +
  geom_histogram(aes(injured_avg_KC), alpha=0.7, fill="#CFB87C") +
  geom_histogram(aes(uninjured_avg_KC), alpha=0.7) +
  labs(title = "Estimated Average Kendall Rank Correlation Coefficient",
       x = "Average Kendall Coefficient")
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

remove(i, p, df, Kendall_Cor, kendall_cors, trends)
Warning in remove(i, p, df, Kendall_Cor, kendall_cors, trends) :
  object 'i' not found
Warning in remove(i, p, df, Kendall_Cor, kendall_cors, trends) :
  object 'p' not found

Is running imbalance sensitive enough of a metric to use as a prognosis tool versus a rehab tool?

Based on the analysis below, we can see that by solely using variance in running imbalance from the time of the injury to the end of the predicted return-to-play range is not a very strong predictor for whether or not it will take longer for a player to recover or not. In this analysis, we used the running imbalance in the time frame starting with the injury date to the end of the prognosis time frame. With these specific running imbalance values, we calculated the variance in running imbalance and whether or not the athlete returned to play within the predicted time frame or not. This analysis found that when using the variance in these time frames as the only predictor in a logistic regression model, the slope coefficient associated with the variance was statistically significant at the \(\alpha = 0.01\) significance level. With that though, the cross validated accuracy of the model was only around 0.6 suggesting that it wasn’t super strong in practice. In order to understand the impact that variance in running imbalance had on whether or not a player returned in the predicted time frame or not, we performed a bootstrap. We separated the observations in which players did and did not return in the predicted time frame into two different data sets. We then sampled from each of these two data sets and calculated the average variance for each sample. This was repeated 5000 times. This gave us an estimate of the average variance in running imbalance for players who returned within the predicted time frame and those who did not. This bootstrap revealed that players who did not return within the predicted time frame had a variance greater by roughly 1.2 during their time of recovery than those who returned on time. This tells us that while variance in running imbalance is not directly strong enough to predict whether or not an athlete will return within the predicted time frame, it can be used to supplement prognosis or make adjustments to the prognosis during the time of recovery of a HSI.

#Calculating date back to play in incident report data set
Warning messages:
1: In png(filename = "C:/Users/cego6188/Downloads/Q1.Pos.Injuries.Bar.png",  :
  unable to open file 'C:/Users/cego6188/Downloads/Q1.Pos.Injuries.Bar.png' for writing
2: In png(filename = "C:/Users/cego6188/Downloads/Q1.Pos.Injuries.Bar.png",  :
  opening device failed
Incident_Report_clean <- Incident_Report_clean %>%
  filter(!is.na(Injury.Prognosis))%>%
  #calculating how long predicted time loss is based on prognosis
         #beginning of predicted range of return
  mutate(Expected.Start.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury,
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(7),
                                                      Date.of.Injury+days(28))))),
         #end of predicted range of return
         Expected.End.Return = as.Date(ifelse(Injury.Prognosis=="No Expected Time Loss",
                                        Date.of.Injury,
                                        ifelse(Injury.Prognosis=="Less than 1 Week",
                                               Date.of.Injury+days(7),
                                               ifelse(Injury.Prognosis=="1-4 Weeks",
                                                      Date.of.Injury+days(28),
                                                      Date.of.Injury+days(56)))))) %>%
  group_by(anon_id, Date.of.Injury) %>%
  #calculating actual date cleared to return
  mutate(Actual.Return = Date.of.Injury+days(sum(na.omit(Days.in.Status)))) %>%
  ungroup()
for(i in 1:22){
  #looking at mean running imbalance per week before and after injury for each injured player
  p <- ggplot(data=Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],], aes(Date, Running.Imbalance)) +
  geom_line() +
    #Marking when injury occurred with red line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury, color="#CFB87C") +
    #Marking actual return date with solid green line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Actual.Return, color="#8D7334", linetype=1) +
  #Marking beginning of predicted return to play range with purple dotted line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.Start.Return, color="#A2A4A3", linetype=3) +
    #Marking end of predicted return to play range with purple dotted line
  geom_vline(xintercept=Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.End.Return, color="#A2A4A3", linetype=3) +
    labs(title=injured_IDs[i]) +
    annotate("rect", 
             xmin = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.Start.Return,
             xmax = Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Expected.End.Return,
             ymin=min(Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],]$Running.Imbalance), ymax=max(Historical_Running_clean[Historical_Running_clean$anon_id == injured_IDs[i],]$Running.Imbalance), alpha=0.3) +
    theme_minimal()
  
  print(p)
}

Focus on 291 for outside and 285 for inside

#looking at running imbalance of only injured players
Injured_Historical_Running <- Historical_Running_clean %>%
  filter(anon_id %in% injured_IDs)

#Making binary column if date was in time range of injury prognosis
Injured_Historical_Running$Date.in.Range <- rep(0, 1658)

#go through all of the injured players in the data set
for(i in 1:22){
  #get the dates each player was injured
  injury_dates <- unique(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i],]$Date.of.Injury)
  
  #go through dates of injury for each player
  for(j in 1:length(injury_dates)){
    if(injured_IDs[i] == "ID_50"){ #ID_50 does not have enough running imbalance data
      break
    }
    #get the expected date of return for that instance of injury
    expected_return <- as.Date(Incident_Report_clean[Incident_Report_clean$anon_id==injured_IDs[i] & Incident_Report_clean$Date.of.Injury==injury_dates[j],]$Expected.End.Return[1])
    
    #if the date in running imbalance is between day of injury and last day of prediction, set as 1
    Injured_Historical_Running[Injured_Historical_Running$anon_id==injured_IDs[i] & Injured_Historical_Running$Date >= injury_dates[j] & Injured_Historical_Running$Date <= expected_return,]$Date.in.Range <- 1
  }
}

#making a column for the variance in running imbalance for each injury instance range
Injured_Historical_Running <- Injured_Historical_Running %>%
  filter(Date.in.Range == 1) %>%
  group_by(anon_id, Injury.Count) %>%
  mutate(Injury.Variability = var(Running.Imbalance)) %>%
  ungroup()

#adding injury and running data sets together
Injured_Data <- left_join(Injured_Historical_Running, Incident_Report_clean, by=c("anon_id", "Injury.Count"), relationship = "many-to-many") %>%
  #making new column if player returned in predicted time frame
  mutate(Return.in.Range = ifelse(Actual.Return>=Expected.Start.Return & Actual.Return <= Expected.End.Return, 1, 0)) %>%
  #removing rows that are missing important data
  filter(!is.na(Injury.Variability),
         !is.na(Return.in.Range))

Logistic Regression Model

set.seed(1000)
#making a 75% to 25% training to testing split
rows <- sample(1:nrow(Injured_Data), size=(nrow(Injured_Data)*0.75), replace=FALSE)
Injured_train <- Injured_Data[rows,]
Injured_test <- Injured_Data[-rows,]

#building logistic regression model from training data
return_to_play_model <- glm(Return.in.Range~Injury.Variability, data=Injured_train, family="binomial")

#looking at coefficients and p-values
summary(return_to_play_model)

Call:
glm(formula = Return.in.Range ~ Injury.Variability, family = "binomial", 
    data = Injured_train)

Coefficients:
                   Estimate Std. Error z value Pr(>|z|)   
(Intercept)        -0.20131    0.12870  -1.564  0.11778   
Injury.Variability -0.04135    0.01521  -2.719  0.00655 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 783.40  on 587  degrees of freedom
Residual deviance: 775.81  on 586  degrees of freedom
AIC: 779.81

Number of Fisher Scoring iterations: 4
#making predictions on testing data based off of the model built
Injured_test <- Injured_test %>%
  mutate(Prediction = ifelse(predict(return_to_play_model, newdata=Injured_test, type="response")>0.5, 1, 0))

#calculating CER
Injured_test %>%
  summarize(CER = mean(Prediction != Return.in.Range))
NA
#making model with all of the data
return_to_play_cv <- glm(Return.in.Range~Injury.Variability, data=Injured_Data, family="binomial")

#making cost function for CER
cost <- function(obs, pred){
  mean((pred <= 0.5) & obs==1 | (pred > 0.5) & obs==0)
}

set.seed(1000)

#cross validating with K=10
ten_cv <- cv.glm(data=Injured_Data,glmfit=return_to_play_cv,cost,K=10)

#extract average error
ten_cv$delta[1]
[1] 0.3923567

Bootstrapping Differences Between In and Out of Range Return to Plays

#taking only players who recovered in predicted time frame
In_Range <- Injured_Data %>%
  filter(Return.in.Range == 1,
         !is.na(Injury.Variability))

#taking only players who did not recover in the predicted time frame
Out_Range <- Injured_Data %>%
  filter(Return.in.Range == 0,
         !is.na(Injury.Variability))
#making data frame to hold values
Range_Variances <- data.frame(in.avg.var = rep(NA, 5000),
                              out.avg.var = rep(NA, 5000),
                              diff.avg.var = rep(NA, 5000))

#bootstrapping 5000 times
for(i in 1:5000){
  set.seed(i)
  
  #sample from players who recovered in predicted range
  in_sample <- sample_n(In_Range, size=308, replace=TRUE)
  #sample fro players who did not recover in predicted range
  out_sample <- sample_n(Out_Range, size=477, replace=TRUE)
  
  
  #calculating variances of each sample and storing in data frame
  Range_Variances[i,1] <- mean(in_sample$Injury.Variability)
  Range_Variances[i,2] <- mean(out_sample$Injury.Variability)
  Range_Variances[i,3] <- Range_Variances[i,2] - Range_Variances[i,1] #diff in variances
}
#plotting differences in average variance
ggplot(data=Range_Variances, aes(diff.avg.var)) +
  geom_histogram() +
  #adding in 90% CI (does not include 0)
  geom_vline(xintercept = quantile(Range_Variances$diff.avg.var, 0.05), color ="#CFB87C") +
  geom_vline(xintercept = quantile(Range_Variances$diff.avg.var, 0.95), color ="#CFB87C") +
  labs(title = "Estimated Difference in Average Variance of Running Imbalance",
       x= "Difference in Average Variance Running Imbalance") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#plotting average variances of difference groups
ggplot(data=Range_Variances) +
  #players who recovered in predicted time frame
  geom_histogram(aes(in.avg.var), alpha = 0.75) +
  #players who did not recover in the predicted time frame
  geom_histogram(aes(out.avg.var), alpha = 0.75, fill ="#CFB87C") +
  labs(title = "Estimated Average Variance in Running Imbalance", 
       x="Average Variance in Running Imbalance") +
  theme_minimal()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

remove(p,i,j,rows,ten_cv, cost, Injured_Data, Injured_Historical_Running, Injured_test, Injured_train, Range_Variances, return_to_play_cv, return_to_play_model, expected_return, injury_dates, In_Range, Out_Range, in_sample, out_sample)
LS0tDQp0aXRsZTogIkRhdGEgQW5hbHlzaXMgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCiNBdXRob3JzOiBJYW4gTWNFbHZlZW4gYW5kIENlY2lsaWEgR29uemFsZXMNCiNBdXRob3IgRGF0ZTogNy8xNC8yMDI1DQojUHVycG9zZTogVGhlIHB1cnBvc2Ugb2YgdGhpcyBub3RlYm9vayBpcyB0byBob3VzZSBhbGwgZGF0YSBzZXQgdHJhbnNmb3JtYXRpb24sIGNsZWFuc2luZywgdmlzdWFsaXphdGlvbiwgc3RhdGlzdGljYWwgYW5hbHlzaXMsIGFuZCBub3RlLXRha2luZyBmb3IgdGhlIDIwMjUgQ1UgQXRobGV0aWMgRGVwYXJ0bWVudCBTcG9ydHMgU2NpZW5jZSBJbnRlcm5zaGlwIFByb2dyYW0NCg0KI0xBU1QgVVBEQVRFRDogNy8yOS8yMDI1DQoNCiNJbmNsdWRpbmcgaGVscGZ1bCBsaWJyYXJpZXMNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGFvZCkNCmxpYnJhcnkoZ3QpDQpsaWJyYXJ5KGJvb3QpDQpsaWJyYXJ5KG1nY3YpDQpsaWJyYXJ5KGxtZTQpDQpsaWJyYXJ5KGxlYXBzKQ0KYGBgDQoNCg0KIyBEYXRhIENsZWFuaW5nDQpgYGB7ciBMb2FkaW5nIGluIHRoZSBkYXRhIHNldHN9DQojbG9hZGluZyBpbiB0aGUgQ2F0YXB1bHQgZGF0YSB0byBsb29rIGF0IHNwcmludGluZyB2YWx1ZXMNCkNhdGFwdWx0X1Nlc3Npb24gPC0gcmVhZF9jc3YoImRhdGEtc2V0cy9kYXRhLXNldHMtdW5jb21wcmVzc2VkL2RhdGEtc2V0cy1jb21wcmVzc2VkL1J1bm5pbmcgSW1iYWxhbmNlIGFuZCBTcGVlZC9DYXRhcHVsdCBTZXNzaW9uIC0gT3V0ZG9vciBGQi5jc3YiKQ0KDQojbG9hZGluZyBpbiB0aGUgSGlzdG9yaWNhbCBSdW5uaW5nIGRhdGEgdG8gbG9vayBhdCBydW5uaW5nIGltYmFsYW5jZSB2YWx1ZXMNCkhpc3RvcmljYWxfUnVubmluZyA8LSByZWFkX2NzdigiZGF0YS1zZXRzL2RhdGEtc2V0cy11bmNvbXByZXNzZWQvZGF0YS1zZXRzLWNvbXByZXNzZWQvUnVubmluZyBJbWJhbGFuY2UgYW5kIFNwZWVkL0NvbXBpbGVkIEhpc3RvcmljYWwgUnVubmluZyBJbWJhbGFuY2UgRkIuY3N2IikNCg0KI2xvYWRpbmcgaW4gdGhlIEluY2lkZW50IFJlcG9ydCB0byBsb29rIGF0IEhTSXMNCkluY2lkZW50X1JlcG9ydCA8LSByZWFkX2NzdigiZGF0YS1zZXRzL2RhdGEtc2V0cy11bmNvbXByZXNzZWQvZGF0YS1zZXRzLWNvbXByZXNzZWQvUnVubmluZyBJbWJhbGFuY2UgYW5kIFNwZWVkL0luY2lkZW50IFJlcG9ydCBGQiBJRHMuY3N2IikNCg0KYGBgDQoNCmBgYHtyIENsZWFuaW5nIENhdGFwdWx0X1Nlc3Npb259DQpDYXRhcHVsdF9TZXNzaW9uX2NsZWFuIDwtIENhdGFwdWx0X1Nlc3Npb24gJT4lDQogICNwdXR0aW5nIHRoZSBkYXRlIGFzIGEgZGF0ZSBjbGFzcw0KICBtdXRhdGUoRGF0ZSA9IGFzLkRhdGUoRGF0ZSwgIiVtLyVkLyVZIikpICU+JQ0KICAjb25seSBzZWxlY3RpbmcgaW1wb3J0YW50IGNvbHVtbnMgZm9yIHRoaXMgYW5hbHlzaXMNCiAgc2VsZWN0KGFub25faWQsIERhdGUsIEFnZSwgUHJpbWFyeS5Qb3NpdGlvbiwgVG90YWwuRGlzdGFuY2UsIFBlcmlvZC5OYW1lLCBUb3RhbC5EdXJhdGlvbi4ubWluLiwgVmVsb2NpdHkuQmFuZC4xLlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjIuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuMy5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC40LlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjUuVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuNi5Ub3RhbC5EaXN0YW5jZSwgVmVsb2NpdHkuQmFuZC43LlRvdGFsLkRpc3RhbmNlLCBWZWxvY2l0eS5CYW5kLjguVG90YWwuRGlzdGFuY2UsIFZlbG9jaXR5LkJhbmQuMi5Ub3RhbC5FZmZvcnQuQ291bnQsIFZlbG9jaXR5LkJhbmQuMy5Ub3RhbC5FZmZvcnQuQ291bnQsIFZlbG9jaXR5LkJhbmQuNC5Ub3RhbC5FZmZvcnQuQ291bnQsIFZlbG9jaXR5LkJhbmQuNS5Ub3RhbC5FZmZvcnQuQ291bnQsIFZlbG9jaXR5LkJhbmQuNi5Ub3RhbC5FZmZvcnQuQ291bnQsIFZlbG9jaXR5LkJhbmQuNy5Ub3RhbC5FZmZvcnQuQ291bnQsIFZlbG9jaXR5LkJhbmQuOC5Ub3RhbC5FZmZvcnQuQ291bnQsIE1heGltdW0uVmVsb2NpdHksIEF2ZXJhZ2UuVmVsb2NpdHksIEhpdC45MC5QZXJjZW50Lk1heCwgRGF0ZS5vZi5MYXN0LjkwLkVmZm9ydCwgRGF5cy5TaW5jZS5MYXN0LjkwLkVmZm9ydCwgSGl0Lk1heC5WZWxvY2l0eS4sIERhdGUub2YuQWxsLlRpbWUuTWF4LlZlbG9jaXR5LCBEYXlzLlNpbmNlLk1heC5WZWxvY2l0eSwgU2Vzc2lvbi5NYXguVmVsb2NpdHkpICU+JQ0KICAjY2FsY3VsYXRpbmcgZWFjaCBwbGF5ZXIncyBtYXhpbXVtIHZlbG9jaXR5DQogIGdyb3VwX2J5KGFub25faWQpICU+JQ0KICBtdXRhdGUoUGxheWVyLk1heC5WZWxvY2l0eSA9IG1heChuYS5vbWl0KE1heGltdW0uVmVsb2NpdHkpKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgI29ubHkgc2VsZWN0aW5nIGRhdGEgZnJvbSBKYW51YXJ5IDEsIDIwMjQgYW5kIG9uDQogIGZpbHRlcihEYXRlID49ICIyMDI0LTAxLTAxIikNCg0KaGVhZChDYXRhcHVsdF9TZXNzaW9uX2NsZWFuKQ0KYGBgDQoNCmBgYHtyIENsZWFuaW5nIEhpc3RvcmljYWxfUnVubmluZ30NCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiA8LSBIaXN0b3JpY2FsX1J1bm5pbmcgJT4lDQogICN0YWtpbmcgb3V0IHJvd3MgdGhhdCBkb24ndCBoYXZlIGRhdGENCiAgZmlsdGVyKFJ1bm5pbmcuSW1iYWxhbmNlICE9ICJuL2EiKSAlPiUNCiAgI3B1dHRpbmcgcnVubmluZyBpbWJhbGFuY2UgYXMgYSBudW1iZXIgYW5kIGNvbnZlcnRpbmcgdGhlIGRhdGUgdG8gYSBkYXRlIGNsYXNzDQogIG11dGF0ZShSdW5uaW5nLkltYmFsYW5jZSA9IGFzLm51bWVyaWMoUnVubmluZy5JbWJhbGFuY2UpLA0KICAgICAgICAgRGF0ZSA9IGFzLkRhdGUoRGF0ZSwgIiVtLyVkLyVZIikpICU+JQ0KICAjb25seSB1c2luZyBkYXRhIGZyb20gSmFudWFyeSAxLCAyMDI0IGFuZCBvbg0KICBmaWx0ZXIoRGF0ZSA+PSAiMjAyNC0wMS0wMSIpICU+JQ0KICBtdXRhdGUoWD0xOjQwNjMpICU+JQ0KICAjbWFraW5nIGRheXMgc2luY2UgSmFudWFyeSAxLCAyMDI0IGZvciBlYWNoIHBsYXllcg0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKERheXMuU2luY2UuU3RhcnQgPSBhcy5udW1lcmljKERhdGUgLSBtaW4oRGF0ZSkpKSAlPiUNCiAgdW5ncm91cCgpDQoNCmhlYWQoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuKQ0KYGBgDQoNCmBgYHtyIENsZWFuaW5nIEluY2lkZW50X1JlcG9ydH0NCkluY2lkZW50X1JlcG9ydF9jbGVhbiA8LSBJbmNpZGVudF9SZXBvcnQgJT4lDQogICNmaWx0ZXJpbmcgZm9yIG9ubHkgaGFtc3RyaW5nIGluanVyaWVzDQogIGZpbHRlcihPU0lDUzE0LkNvZGUgPT0gIlRNMSIsDQogICAgICAgICBTdGF0dXMgIT0gIkZ1bGwgR28iKSAgJT4lDQogICNnZXR0aW5nIHRoZSBkYXRlIG9mIHRoZSBpbmp1cnkgYXMgYSBkYXRlIGNsYXNzDQogIG11dGF0ZShEYXRlID0gYXMuRGF0ZShEYXRlLCAiJW0vJWQvJVkiKSwNCiAgICAgICAgIERhdGUub2YuSW5qdXJ5ID0gYXMuRGF0ZShEYXRlLm9mLkluanVyeS4uLk9uc2V0Lm9mLnN5bXB0b21zLCAiJW0vJWQvJVkiKSwNCiAgICAgICAgIEV4YW1pbmF0aW9uLkRhdGUgPSBhcy5EYXRlKEV4YW1pbmF0aW9uLkRhdGUsICIlbS8lZC8lWSIpKSAlPiUNCiAgI29ubHkgc2VsZWN0aW5nIHJlbGV2YW50IGNvbHVtbnMgZm9yIHRoaXMgYW5hbHlzaXMNCiAgc2VsZWN0KGFub25faWQsIFBvc2l0aW9uLCBEYXRlLCBEYXRlLm9mLkluanVyeSwgVGltZS5vZi5Jbmp1cnksIFNpZGUsIE9TSUNTLkluanVyeS5EaWFnbm9zaXMsIENvYWNoLnMuRGlhZ25vc2lzLCBSZWN1cnJlbmNlLm9mLkluanVyeSwgQ2hvb3NlLlNlYXNvbiwgT25zZXQub2YuU3ltcHRvbXMsIEluanVyeS5Qcm9nbm9zaXMsIEdlbmVyYWwuTWVjaGFuaXNtLCBTcGVjaWZpYy5NZWNoYW5pc20sIEluanVyZWQuV2hpbGUuLCBUeXBlLm9mLkV2ZW50LCBTZWFzb24uLCBTdGF0dXMsIERheXMuaW4uU3RhdHVzKSAlPiUNCiAgI21ha2luZyBkYXlzIG91dCBkdWUgdG8gaW5qdXJ5IGZvciBlYWNoIHBsYXllciBhbmQgZWFjaCBpbmp1cnkgdGhleSBzdXN0YWluZWQNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgRGF0ZS5vZi5Jbmp1cnkpICU+JQ0KICBtdXRhdGUoRGF5cy5PdXQgPSBzdW0oRGF5cy5pbi5TdGF0dXMpKSAlPiUNCiAgdW5ncm91cCgpDQoNCmhlYWQoSW5jaWRlbnRfUmVwb3J0X2NsZWFuKQ0KYGBgDQoNCmBgYHtyIElkZW50aWZ5aW5nIHBsYXllcnMgdGhhdCBoYXZlIGRhdGEgaW4gYm90aCBkYXRhIHNldHN9DQojdGFraW5nIHRoZSBJRHMgb2YgcGxheWVycyB3aG8gYXJlIGFuZCBhcmVuJ3QgaW5qdXJlZA0KYWxsX0lEcyA8LSB1bmlxdWUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQpDQojdGFraW5nIElEcyB0aGF0IHdlcmUgaW5qdXJlZCBhbmQgYWxzbyBoYXZlIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGENCmluanVyZWRfSURzIDwtIGludGVyc2VjdCh1bmlxdWUoSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQpLCBhbGxfSURzKQ0KI3Rha2luZyBhbGwgcGxheWVycyB3aXRoIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEgdGhhdCBkb24ndCBoYXZlIGFuIGluanVyeQ0KdW5pbmp1cmVkX0lEcyA8LSB1bmlxdWUoKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZmlsdGVyKCFhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMpKSRhbm9uX2lkKQ0KYGBgDQoNCmBgYHtyIEZpbHRlcmluZyBvdXQgcGxheWVycyB3aXRob3V0IHByb3BlciBkYXRhIGZyb20gYWxsIGRhdGEgc2V0c30NCiNpbmp1cmVkIHBsYXllcnMgd2hvIGFsc28gaGF2ZSBydW5uaW5nIGltYmFsYW5jZSBkYXRhDQpJbmNpZGVudF9SZXBvcnRfY2xlYW4gPC0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCAlaW4lIGluanVyZWRfSURzKQ0KDQojYWxsIHBsYXllcnMgdGhhdCBvbmx5IGhhdmUgcnVubmluZyBpbWJhbGFuY2UgZGF0YSBvciBoYXZlIGJvdGggcnVubmluZyBpbWJhbGFuY2UgZGF0YSBhbmQgaW5jaWRlbmNlIHJlcG9ydA0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgJWluJSBpbmp1cmVkX0lEcyB8IGFub25faWQgJWluJSB1bmluanVyZWRfSURzKQ0KYGBgDQoNCmBgYHtyIFJlbW92aW5nIGFsbCB1bmltcG9ydGFudCBvYmplY3RzIGNyZWF0ZWQgZHVyaW5nIGNsZWFuaW5nfQ0KI3JlbW92aW5nIHVuY2xlYW5lZCBkYXRhIHNldHMNCnJlbW92ZShJbmNpZGVudF9SZXBvcnQpDQpyZW1vdmUoSGlzdG9yaWNhbF9SdW5uaW5nKQ0KcmVtb3ZlKENhdGFwdWx0X1Nlc3Npb24pDQpgYGANCg0KIyBTZWN0aW9uIDE6IFJ1bm5pbmcgU3BlZWQNCg0KIyMgSG93IG9mdGVuIGFyZSBhdGhsZXRlcyByZWFjaGluZyDiiaUgOTAlIG1heGltdW0gdmVsb2NpdHkgdGhyb3VnaG91dCBhIHRyYWluaW5nIHNlYXNvbj8NCg0KYGBge3J9DQojIEJhciBjaGFydCBmb3IgaG93IG9mdGVuIHBsYXllcnMgcmVhY2gg4omlIDkwJSBtYXhpbXVtIHZlbG9jaXR5DQoNCiMgQ291bnQgZm9yIGhvdyBtYW55IHRpbWVzIGVhY2ggYW5vbl9pZCBoaXQg4omlIDkwJSBtYXhpbXVtIHZlbG9jaXR5DQpoaXRfOTBfY291bnRzIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihEYXRlID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUgIyBGaWx0ZXIgZm9yIHRyYWluaW5nIHNlYXNvbg0KICBmaWx0ZXIoSGl0LjkwLlBlcmNlbnQuTWF4ID09ICJZZXMiKSAlPiUNCiAgZGlzdGluY3QoYW5vbl9pZCwgRGF0ZSwgUHJpbWFyeS5Qb3NpdGlvbikgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIFByaW1hcnkuUG9zaXRpb24pICU+JQ0KICBzdW1tYXJpc2UodGltZXNfaGl0XzkwID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgbXV0YXRlKA0KICAgIFBvc2l0aW9uX0dyb3VwID0gY2FzZV93aGVuKA0KICAgICAgUHJpbWFyeS5Qb3NpdGlvbiAlaW4lIGMoIlFCIiwgIkxCIiwgIlRFIiwgIlJCIikgfiAiQ09NQk8iLA0KICAgICAgUHJpbWFyeS5Qb3NpdGlvbiAlaW4lIGMoIk9MIiwgIkRMIiwgIkRFIikgfiAiQklHUyIsDQogICAgICBQcmltYXJ5LlBvc2l0aW9uICVpbiUgYygiV1IiLCAiREIiLCAiREIsIFdSIikgfiAiU0tJTEwiLA0KICAgICAgVFJVRSB+ICJPVEhFUiINCiAgICApKQ0KDQojIFBsb3Qgb2YgYWxsIHBsYXllcnMnIGZyZXF1ZW5jaWVzDQpnZ3Bsb3QoaGl0XzkwX2NvdW50cywgYWVzKHggPSBhbm9uX2lkLCB5ID0gdGltZXNfaGl0XzkwKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuKGhpdF85MF9jb3VudHMkdGltZXNfaGl0XzkwKSwgDQogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiIzU2NUE1QyIsIGxpbmV3aWR0aCA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIlBsYXllciBDb3VudHMgZm9yIEFjaGlldmluZyDiiaUgOTAlIG9mIE1heGltdW0gVmVsb2NpdHkgRHVyaW5nIDIwMjTigJMyNSBTZWFzb24iLCANCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJUZWFtIEF2ZXJhZ2U6Iiwgcm91bmQobWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCksIDEpKSwNCiAgICAgICB4ID0gIkF0aGxldGUgSUQiLCB5ID0gIlRpbWVzIOKJpSA5MCUiKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2LCBhbmdsZSA9IDkwKSkNCmBgYA0KIyBEbyBub3QgbmVlZCBiZWNhdXNlIHdlIGNyZWF0ZWQgYSBmdW5jdGlvbiB0byBwbG90IGVhY2ggcG9zaXRpb24NCmBgYHtyfQ0KIyBCYXIgY2hhcnQgb2YgdGltZXMgcmVhY2hlZCA+OTAlLCBRdWFydGVyYmFja3MNCg0KIyBGaWx0ZXIgdG8gb25seSBoYXZlIGRhdGEgZm9yIFFCcw0KUUJzIDwtIGhpdF85MF9jb3VudHMgJT4lDQogIGZpbHRlcihQcmltYXJ5LlBvc2l0aW9uID09ICJRQiIpDQoNCiMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlcyBmaXJzdA0Kb3ZlcmFsbF9hdmcgPC0gbWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCkNCnFiX2F2ZyA8LSBtZWFuKFFCcyR0aW1lc19oaXRfOTApDQoNCmdncGxvdChRQnMsIGFlcyh4PWFub25faWQsIHk9dGltZXNfaGl0XzkwKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGwgPSAiI0NGQjg3QyIpICsgDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSB0aW1lc19oaXRfOTApLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgDQogICAgICAgICAgICBzaXplID0gMy41KSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG92ZXJhbGxfYXZnLCANCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjMDAwMDAwIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBxYl9hdmcsIA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiM1NjVBNUMiKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSBvdmVyYWxsX2F2ZyArIDAuNSwgDQogICAgICAgICAgIGxhYmVsID0gIlRlYW0gQXZnIiwgY29sb3IgPSAiIzAwMDAwMCIsIHNpemUgPSAzKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSBxYl9hdmcgKyAwLjUsIA0KICAgICAgICAgICBsYWJlbCA9ICJRQiBBdmciLCBjb2xvciA9ICIjNTY1QTVDIiwgc2l6ZSA9IDMpICsNCiAgbGFicyh0aXRsZSA9ICJRQiBDb3VudHMgZm9yIFJlYWNoaW5nIOKJpTkwJSBNYXggVmVsb2NpdHkiLCANCiAgICAgICB4ID0gIkF0aGxldGUgSUQiLCB5ID0gIlRpbWVzIOKJpTkwJSIsDQogICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiUUIgQXZlcmFnZToiLCBxYl9hdmcpKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQpgYGANCg0KIyMgRmFjZXQgcGxvdCB1c2VmdWwsIGJ1dCBoYXJkIHRvIHJlYWQgc28gcHJvYmFibHkgZG9uJ3QgaW5jbHVkZSBpbiBwcmVzZW50YXRpb24uDQpgYGB7cn0NCiMgRmFjZXQgUGxvdA0KDQojIENhbGN1bGF0ZSBvdmVyYWxsIGF2ZXJhZ2UNCm92ZXJhbGxfYXZnIDwtIG1lYW4oaGl0XzkwX2NvdW50cyR0aW1lc19oaXRfOTApDQoNCiMgUGxvdCBmYWNldGVkIGJhciBjaGFydHMgYnkgcG9zaXRpb24NCmdncGxvdChoaXRfOTBfY291bnRzLCBhZXMoeCA9IGFub25faWQsIHkgPSB0aW1lc19oaXRfOTApKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNDRkI4N0MiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSB0aW1lc19oaXRfOTApLCB2anVzdCA9IC0wLjUsIHNpemUgPSAzLjUpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gb3ZlcmFsbF9hdmcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiMwMDAwMDAiKSArDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSBvdmVyYWxsX2F2ZyArIDAuNSwgDQogICAgICAgICAgIGxhYmVsID0gIlRlYW0gQXZnIiwgY29sb3IgPSAiIzAwMDAwMCIsIHNpemUgPSAzLCBoanVzdCA9IDApICsNCiAgZmFjZXRfd3JhcCh+IFByaW1hcnkuUG9zaXRpb24sIHNjYWxlcyA9ICJmcmVlX3giKSArDQogIGxhYnModGl0bGUgPSAiQ291bnRzIG9mIOKJpTkwJSBNYXggVmVsb2NpdHkgYnkgUGxheWVyIGFuZCBQb3NpdGlvbiIsDQogICAgICAgeSA9ICJUaW1lcyDiiaU5MCUgTWF4IFZlbG9jaXR5IiwNCiAgICAgICB4ID0gIkF0aGxldGUgSUQiKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KYGBge3J9DQojIFBsb3RzIGZvciBlYWNoIHBvc2l0aW9uDQoNCiMgR2V0IHRoZSBvdmVyYWxsIHRlYW0gYXZlcmFnZSBvbmNlIGZyb20gaGl0XzkwX2NvdW50cw0KdGVhbV9hdmcgPC0gbWVhbihoaXRfOTBfY291bnRzJHRpbWVzX2hpdF85MCkNCg0KIyBEZWZpbmUgIHBvc2l0aW9ucyB0byBsb29wIHRocm91Z2gNCnBvc2l0aW9ucyA8LSB1bmlxdWUoaGl0XzkwX2NvdW50cyRQcmltYXJ5LlBvc2l0aW9uKQ0KDQojIFBsb3R0aW5nIGZ1bmN0aW9uOg0KcGxvdF9oaXRfOTBfYnlfcG9zaXRpb24gPC0gZnVuY3Rpb24ocG9zKSB7DQogIHBvc2l0aW9uX2RhdGEgPC0gaGl0XzkwX2NvdW50cyAlPiUNCiAgICBmaWx0ZXIoUHJpbWFyeS5Qb3NpdGlvbiA9PSBwb3MpDQogIA0KICBwb3NfYXZnIDwtIG1lYW4ocG9zaXRpb25fZGF0YSR0aW1lc19oaXRfOTApDQogIA0KICBnZ3Bsb3QocG9zaXRpb25fZGF0YSwgYWVzKHggPSBhbm9uX2lkLCB5ID0gdGltZXNfaGl0XzkwKSkgKw0KICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNDRkI4N0MiKSArDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHRpbWVzX2hpdF85MCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMuNSkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHBvc19hdmcsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiM1NjVBNUMiKSArDQogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gMSwgeSA9IHBvc19hdmcgKyAwLjUsIA0KICAgICAgICAgICAgIGxhYmVsID0gIlBvcyBBdmciLCBjb2xvciA9ICIjNTY1QTVDIiwgc2l6ZSA9IDMsIGhqdXN0ID0gMCkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fYXZnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjMDAwMDAwIikgKw0KICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSB0ZWFtX2F2ZyArIDAuNSwgDQogICAgICAgICAgICAgbGFiZWwgPSAiVGVhbSBBdmciLCBjb2xvciA9ICIjMDAwMDAwIiwgc2l6ZSA9IDMsIGhqdXN0ID0gMCkgKw0KICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiVGltZXMg4omlOTAlIE1heCBWZWxvY2l0eSDigJMiLCBwb3MpLA0KICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIlBvc2l0aW9uIEF2ZzogIiwgcm91bmQocG9zX2F2ZywgMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiIHwgVGVhbSBBdmc6ICIsIHJvdW5kKHRlYW1fYXZnLCAxKSksDQogICAgICAgICB5ID0gIkNvdW50IiwNCiAgICAgICAgIHggPSAiYW5vbl9pZCIpICsNCiAgICB0aGVtZV9jbGFzc2ljKCkgKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQp9DQoNCiMgTG9vcCB0aHJvdWdoIGVhY2ggcG9zaXRpb24gYW5kIHByaW50IHRoZSBwbG90DQpwbG90c19ieV9wb3NpdGlvbiA8LSBsYXBwbHkocG9zaXRpb25zLCBmdW5jdGlvbihwb3MpIHsNCiAgcHJpbnQocGxvdF9oaXRfOTBfYnlfcG9zaXRpb24ocG9zKSkNCn0pDQoNCmBgYA0KDQpgYGB7cn0NCiMgVGFibGUgZm9yIGF2ZXJhZ2UgdmFsdWVzIG9mIGVhY2ggcG9zaXRpb24NCg0KcG9zaXRpb25fYXZlcmFnZXMgPC0gaGl0XzkwX2NvdW50cyAlPiUNCiAgZ3JvdXBfYnkoUHJpbWFyeS5Qb3NpdGlvbikgJT4lDQogIHN1bW1hcmlzZShhdmdfdGltZXNfaGl0XzkwID0gbWVhbih0aW1lc19oaXRfOTApLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgYXJyYW5nZShkZXNjKGF2Z190aW1lc19oaXRfOTApKQ0KDQpwb3NpdGlvbl9hdmVyYWdlc193aXRoX3RlYW0gPC0gYmluZF9yb3dzKA0KICB0aWJibGUoDQogICAgUHJpbWFyeS5Qb3NpdGlvbiA9ICJUZWFtIEF2ZXJhZ2UiLA0KICAgIGF2Z190aW1lc19oaXRfOTAgPSB0ZWFtX2F2Zw0KICApLA0KICBwb3NpdGlvbl9hdmVyYWdlcw0KKQ0KDQpwb3NpdGlvbl9hdmVyYWdlc193aXRoX3RlYW0gJT4lDQogIGd0KCkgJT4lDQogIHRhYl9oZWFkZXIoDQogICAgdGl0bGUgPSAiQXZlcmFnZSBUaW1lcyDiiaU5MCUgTWF4IFZlbG9jaXR5IGJ5IFBvc2l0aW9uIGFuZCBUZWFtIg0KICApDQpgYGANCg0KREJzIGhhdmUgdGhlIGhpZ2hlc3QgYXZlcmFnZSB0aW1lcyByZWFjaGluZyDiiaU5MCUgb2YgTWF4IHZlbG9jaXR5ICgxOC40KS4gVGhlIE9MIGhhZCB0aGUgbG93ZXN0ICg2LjgpLiBUZWFtIGF2ZXJhZ2Ugd2FzIDExLjguDQoNCmBgYHtyfQ0KIyBQbG90IGNvdW50cyBmb3IgZWFjaCBwb3NpdGlvbiBncm91cA0KDQojIEdldCB0aGUgb3ZlcmFsbCB0ZWFtIGF2ZXJhZ2Ugb25jZSBmcm9tIGhpdF85MF9jb3VudHMNCnRlYW1fYXZnIDwtIG1lYW4oaGl0XzkwX2NvdW50cyR0aW1lc19oaXRfOTApDQoNCiMgRGVmaW5lICBwb3NpdGlvbnMgdG8gbG9vcCB0aHJvdWdoDQpwb3NpdGlvbnNfZ3JvdXBzIDwtIHVuaXF1ZShoaXRfOTBfY291bnRzJFBvc2l0aW9uX0dyb3VwKQ0KDQojIFBsb3R0aW5nIGZ1bmN0aW9uOg0KcGxvdF9oaXRfOTBfZ3JvdXAgPC0gZnVuY3Rpb24ocG9zKSB7DQogIGdyb3VwX2RhdGEgPC0gaGl0XzkwX2NvdW50cyAlPiUNCiAgICBmaWx0ZXIoUG9zaXRpb25fR3JvdXAgPT0gcG9zKQ0KICANCiAgZ3JvdXBfYXZnIDwtIG1lYW4oZ3JvdXBfZGF0YSR0aW1lc19oaXRfOTApDQogIA0KICBnZ3Bsb3QoZ3JvdXBfZGF0YSwgYWVzKHggPSBhbm9uX2lkLCB5ID0gdGltZXNfaGl0XzkwKSkgKw0KICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIiNDRkI4N0MiKSArDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHRpbWVzX2hpdF85MCksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMuNSkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGdyb3VwX2F2ZywgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiIzU2NUE1QyIpICsNCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxLCB5ID0gZ3JvdXBfYXZnICsgMC41LCANCiAgICAgICAgICAgICBsYWJlbCA9ICJHcm91cCBBdmciLCBjb2xvciA9ICIjNTY1QTVDIiwgc2l6ZSA9IDMsIGhqdXN0ID0gMCkgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fYXZnLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjMDAwMDAwIikgKw0KICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSB0ZWFtX2F2ZyArIDAuNSwgDQogICAgICAgICAgICAgbGFiZWwgPSAiVGVhbSBBdmciLCBjb2xvciA9ICIjMDAwMDAwIiwgc2l6ZSA9IDMsIGhqdXN0ID0gMCkgKw0KICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiVGltZXMg4omlOTAlIE1heCBWZWxvY2l0eSDigJMiLCBwb3MpLA0KICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIkdyb3VwIEF2ZzogIiwgcm91bmQoZ3JvdXBfYXZnLCAxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICIgfCBUZWFtIEF2ZzogIiwgcm91bmQodGVhbV9hdmcsIDEpKSwNCiAgICAgICAgIHkgPSAiQ291bnQiLA0KICAgICAgICAgeCA9ICJhbm9uX2lkIikgKw0KICAgIHRoZW1lX2NsYXNzaWMoKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCn0NCg0KIyBMb29wIHRocm91Z2ggZWFjaCBwb3NpdGlvbiBhbmQgcHJpbnQgdGhlIHBsb3QNCnBsb3RzX2J5X2dyb3VwIDwtIGxhcHBseShwb3NpdGlvbnNfZ3JvdXBzLCBmdW5jdGlvbihwb3MpIHsNCiAgcHJpbnQocGxvdF9oaXRfOTBfZ3JvdXAocG9zKSkNCn0pDQoNCmBgYA0KVGhlIHNraWxsIHBvc2l0aW9uIGdyb3VwIGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIHRpbWVzIHJlYWNoZWQg4omlOTAlIE1heCB3aXRoIDE3LjEsIHRoZW4gY29tYm8gd2l0aCA5LjMsIHRoZW4gYmlncyB3aXRoIDguOS4NCg0KYGBge3J9DQojIENyZWF0ZSBwb3NpdGlvbiBncm91cCBhdmcgY291bnQgdGFibGUNCg0KIyBDcmVhdGUgdGVhbSBhdmVyYWdlIHJvdw0KdGVhbV9yb3cgPC0gdGliYmxlKA0KICBHcm91cCA9ICJUZWFtIEF2ZyIsDQogIEF2ZXJhZ2UgPSB0ZWFtX2F2Zw0KKQ0KDQojIENyZWF0ZSBncm91cCBhdmVyYWdlIHJvd3MNCmdyb3VwX3Jvd3MgPC0gaGl0XzkwX2NvdW50cyAlPiUNCiAgZ3JvdXBfYnkoUG9zaXRpb25fR3JvdXApICU+JQ0KICBzdW1tYXJpc2UoQXZlcmFnZSA9IG1lYW4odGltZXNfaGl0XzkwKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGFycmFuZ2UoZGVzYyhBdmVyYWdlKSkgJT4lICAjIFNvcnQgZnJvbSBoaWdoZXN0IHRvIGxvd2VzdA0KICByZW5hbWUoR3JvdXAgPSBQb3NpdGlvbl9Hcm91cCkNCg0KIyBDb21iaW5lIHRoZW0gKHRlYW0gZmlyc3QpDQpjb21iaW5lZF90YWJsZSA8LSBiaW5kX3Jvd3ModGVhbV9yb3csIGdyb3VwX3Jvd3MpDQoNCiMgVmlldyB0aGUgdGFibGUNCmNvbWJpbmVkX3RhYmxlDQpgYGANCk5vIHN1cnByaXNlIHRoYXQgdGhlIHNraWxsIHBvc2l0aW9uIGdyb3VwIGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIGNvdW50cy4gDQoNCmBgYHtyfQ0KIyA+OTAlIGNvdW50cyBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGhlIHNlYXNvbg0KDQojIFRyeWluZyB0byBmaW5kIG91dCB3aGVuIGFyZSBwbGF5ZXJzIHJlYWNoaW5nIGFib3ZlIDkwJSB0aGUgbW9zdA0KDQojIENyZWF0ZSBkYXRhc2V0IHRoYXQgaGFzIHRoZSBjb3VudCBvZiA+OTAlIG9mIGVhY2ggZGF5DQpkYWlseV85MF9jb3VudHMgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDI1LTA3LTAxIikpICU+JQ0KICBmaWx0ZXIoSGl0LjkwLlBlcmNlbnQuTWF4ID09ICJZZXMiKSAlPiUNCiAgZGlzdGluY3QoYW5vbl9pZCwgRGF0ZSkgJT4lDQogIGdyb3VwX2J5KERhdGUpICU+JQ0KICBzdW1tYXJpc2UoZGFpbHlfaGl0cyA9IG4oKSkNCg0KIyBQbG90DQpnZ3Bsb3QoZGFpbHlfOTBfY291bnRzLCBhZXMoeCA9IERhdGUsIHkgPSBkYWlseV9oaXRzKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiI0NGQjg3QyIsIGxpbmV3aWR0aCA9IDEpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiIzU2NUE1QyIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJEYWlseSBDb3VudCBvZiBQbGF5ZXJzIFJlYWNoaW5nIOKJpSA5MCUgb2YgTWF4IFZlbG9jaXR5IiwNCiAgICAgICBzdWJ0aXRsZSA9ICJPdmVyIHRoZSAyMDI04oCTMjUgVHJhaW5pbmcgU2Vhc29uIiwNCiAgICAgICB4ID0gIkRhdGUiLCB5ID0gIlRpbWVzIHJlYWNoZWQg4omlIDkwJSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCkRhdGEgaXMgbm9pc3ksIHNvIGxldHMgdHJ5IGdyb3VwaW5nIGJ5IHdlZWsgaW5zdGVhZCBvZiBldmVyeSBkYXkuDQoNCmBgYHtyfQ0KIyA+OTAlIGNvdW50cyBmb3IgZWFjaCB3ZWVrIGluc3RlYWQgb2YgZWFjaCBkYXkuIFdpbGwgYmUgYSBsaXR0bGUgbGVzcyBub2lzeSB0aGFuIHRoZSBkYWlseQ0Kd2Vla2x5XzkwX2NvdW50cyA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBmaWx0ZXIoRGF0ZSA+PSBhcy5EYXRlKCIyMDI0LTA2LTMwIikgJiBEYXRlIDw9IGFzLkRhdGUoIjIwMjUtMDctMDEiKSkgJT4lDQogIGZpbHRlcihIaXQuOTAuUGVyY2VudC5NYXggPT0gIlllcyIpICU+JQ0KICBkaXN0aW5jdChhbm9uX2lkLCBEYXRlKSAlPiUNCiAgbXV0YXRlKHdlZWsgPSBmbG9vcl9kYXRlKERhdGUsIHVuaXQgPSAid2VlayIpKSAlPiUNCiAgZ3JvdXBfYnkod2VlaykgJT4lDQogIHN1bW1hcmlzZSh3ZWVrbHlfaGl0cyA9IG4oKSkNCg0KZ2dwbG90KHdlZWtseV85MF9jb3VudHMsIGFlcyh4ID0gd2VlaywgeSA9IHdlZWtseV9oaXRzKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiI0NGQjg3QyIsIGxpbmV3aWR0aCA9IDEpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjNTY1QTVDIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuKHdlZWtseV85MF9jb3VudHMkd2Vla2x5X2hpdHMpLCANCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjNTY1QTVDIiwgbGluZXdpZHRoID0gMC42KSArDQogIGxhYnModGl0bGUgPSAiV2Vla2x5IENvdW50IG9mIFBsYXllcnMgUmVhY2hpbmcg4omlIDkwJSBvZiBNYXggVmVsb2NpdHkiLA0KICAgICAgIHggPSAiV2VlayIsIHkgPSAiVGltZXMgcmVhY2hlZCDiiaUgOTAlIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpgYGB7cn0NCiMgQmFyIGNoYXJ0IG9mIHdlZWtseSA+OTAlIGNvdW50cw0KDQptZWFuX3ZhbCA8LSBtZWFuKHdlZWtseV85MF9jb3VudHMkd2Vla2x5X2hpdHMpDQoNCmdncGxvdCh3ZWVrbHlfOTBfY291bnRzLCBhZXMoeCA9IHdlZWssIHkgPSB3ZWVrbHlfaGl0cykpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjQ0ZCODdDIiwgY29sb3IgPSAiIzU2NUE1QyIsIHdpZHRoID0gNSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gd2Vla2x5X2hpdHMpLCANCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNCwgDQogICAgICAgICAgICBzaXplID0gMi41LCANCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuX3ZhbCwNCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICIjNTY1QTVDIiwgbGluZXdpZHRoID0gMC41KSArDQogIGFubm90YXRlKCJ0ZXh0IiwgDQogICAgICAgICAgIHggPSBtaW4od2Vla2x5XzkwX2NvdW50cyR3ZWVrKSArIDcsICAjIEFkanVzdCB0aGlzIGRhdGUgdG8gcGxhY2UgdGhlIGxhYmVsDQogICAgICAgICAgIHkgPSBtZWFuX3ZhbCArIDEuNSwgICAgICAgICAgICAgICAgICMgU2xpZ2h0bHkgYWJvdmUgdGhlIGxpbmUNCiAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTWVhbjoiLCByb3VuZChtZWFuX3ZhbCwgMSkpLA0KICAgICAgICAgICBzaXplID0gMy4yNSwgY29sb3IgPSAiIzAwMDAwMCIpICsNCiAgbGFicygNCiAgICB0aXRsZSAgICA9ICJXZWVrbHkgQ291bnQgb2YgUGxheWVycyBSZWFjaGluZyDiiaUgOTAlIG9mIE1heCBWZWxvY2l0eSIsDQogICAgc3VidGl0bGUgPSBwYXN0ZSgiU2Vhc29uIEF2ZXJhZ2UgcGVyIFdlZWs6Iiwgcm91bmQobWVhbih3ZWVrbHlfOTBfY291bnRzJHdlZWtseV9oaXRzKSwgMSkpLA0KICAgIHggICAgICAgID0gIldlZWsiLA0KICAgIHkgICAgICAgID0gIlRpbWVzIHJlYWNoZWQg4omlIDkwJSINCiAgKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIyIHdlZWtzIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJWQiKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkNCiAgKQ0KYGBgDQpTb21lIG9mIG91ciBsb3dlc3QgYXZlcmFnZSBjb3VudHMgZm9yIGEgd2VlayBzZWVtIHRvIGJlIGR1cmluZyB0aGUgc2Vhc29uLCB3aGVyZWFzIHNvbWUgb2Ygb3VyIGhpZ2hlc3QgY291bnRzIHNlZW0gdG8gYmUgcHJlIGFuZCBwb3N0LXNlYXNvbi4NCg0KIyMgU2hvdWxkIHdlIGNvbnNpZGVyIHRoZSBudW1iZXIgb2Ygc3ByaW50aW5nIGVmZm9ydHMgdGhhdCBhdGhsZXRlcyBhcmUgY29tcGxldGluZz8NCg0KSG93IG9mdGVuIGF0aGxldGVzIGFyZSBoaXR0aW5nIHdoaWNoIHZlbG9jaXR5IGJhbmRzIGluIHJlbGF0aW9uIHRvIHRoZWlyIHRvcCBzcGVlZHMuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgc3VtIG9mIHdlZWtseSBiYW5kIHRvdGFscw0KDQojIE1ha2Ugc3VyZSB3ZWVrIGlzIGRlZmluZWQNCkNhdGFwdWx0X1Nlc3Npb25fY2xlYW4gPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDI1LTA3LTAxIikpICU+JSANCiAgbXV0YXRlKHdlZWsgPSBmbG9vcl9kYXRlKERhdGUsIHVuaXQgPSAid2VlayIpKQ0KDQojIFN1bSBlZmZvcnRzIGJ5IHdlZWsgYW5kIGF0aGxldGUNCndlZWtseV92ZWxvY2l0eV9lZmZvcnRzIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGdyb3VwX2J5KGFub25faWQsIHdlZWspICU+JQ0KICBzdW1tYXJpc2UoDQogICAgVjIgPSBzdW0oVmVsb2NpdHkuQmFuZC4yLlRvdGFsLkVmZm9ydC5Db3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICBWMyA9IHN1bShWZWxvY2l0eS5CYW5kLjMuVG90YWwuRWZmb3J0LkNvdW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIFY0ID0gc3VtKFZlbG9jaXR5LkJhbmQuNC5Ub3RhbC5FZmZvcnQuQ291bnQsIG5hLnJtID0gVFJVRSksDQogICAgVjUgPSBzdW0oVmVsb2NpdHkuQmFuZC41LlRvdGFsLkVmZm9ydC5Db3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICBWNiA9IHN1bShWZWxvY2l0eS5CYW5kLjYuVG90YWwuRWZmb3J0LkNvdW50LCBuYS5ybSA9IFRSVUUpLA0KICAgIFY3ID0gc3VtKFZlbG9jaXR5LkJhbmQuNy5Ub3RhbC5FZmZvcnQuQ291bnQsIG5hLnJtID0gVFJVRSksDQogICAgVjggPSBzdW0oVmVsb2NpdHkuQmFuZC44LlRvdGFsLkVmZm9ydC5Db3VudCwgbmEucm0gPSBUUlVFKSwNCiAgICB0b3RhbF9lZmZvcnRzID0gVjIgKyBWMyArIFY0ICsgVjUgKyBWNiArIFY3ICsgVjgsDQogICAgV2Vla2x5X01heF9WZWxvY2l0eSA9IG1heChNYXhpbXVtLlZlbG9jaXR5LCBuYS5ybSA9IFRSVUUpLA0KICAgIFBsYXllcl9NYXhfVmVsb2NpdHkgPSBmaXJzdChQbGF5ZXIuTWF4LlZlbG9jaXR5KSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgJT4lDQogIG11dGF0ZSgNCiAgICBwY3Rfb2ZfbWF4X3ZlbG9jaXR5ID0gKFdlZWtseV9NYXhfVmVsb2NpdHkgLyBQbGF5ZXJfTWF4X1ZlbG9jaXR5KSAqIDEwMA0KICApDQoNCiMgQ3JlYXRlIHBlcmNlbnRhZ2Ugb2Ygd2Vla2x5IGVmZm9ydHMgaW4gZWFjaCBiYW5kDQp3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyA8LSB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyAlPiUNCiAgbXV0YXRlKA0KICAgIHBjdF9WMiA9IChWMiAvIHRvdGFsX2VmZm9ydHMpICogMTAwLA0KICAgIHBjdF9WMyA9IChWMyAvIHRvdGFsX2VmZm9ydHMpICogMTAwLA0KICAgIHBjdF9WNCA9IChWNCAvIHRvdGFsX2VmZm9ydHMpICogMTAwLA0KICAgIHBjdF9WNSA9IChWNSAvIHRvdGFsX2VmZm9ydHMpICogMTAwLA0KICAgIHBjdF9WNiA9IChWNiAvIHRvdGFsX2VmZm9ydHMpICogMTAwLA0KICAgIHBjdF9WNyA9IChWNyAvIHRvdGFsX2VmZm9ydHMpICogMTAwLA0KICAgIHBjdF9WOCA9IChWOCAvIHRvdGFsX2VmZm9ydHMpICogMTAwDQogICkNCg0Kd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIGZpbHRlcih0b3RhbF9lZmZvcnRzID4gMCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBQbG90IGZvciBlYWNoIHBsYXllcg0KDQojIExvb3Agb3ZlciBhbGwgcGxheWVycyBhbmQgZGlzcGxheSB0aGVpciBwbG90cw0KdW5pcXVlKHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzJGFub25faWQpICU+JQ0KICBsYXBwbHkoZnVuY3Rpb24ocGxheWVyX2lkKSB7DQogICAgDQogICAgIyBQcmVwYXJlIHRoZSBkYXRhIGZvciBvbmUgcGxheWVyDQogICAgcGxheWVyX3dlZWtseV9kYXRhIDwtIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzICU+JQ0KICAgICAgZmlsdGVyKGFub25faWQgPT0gcGxheWVyX2lkKSAlPiUNCiAgICAgIHNlbGVjdCh3ZWVrLCBwY3RfVjI6cGN0X1Y4LCBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSAlPiUNCiAgICAgIHBpdm90X2xvbmdlcigNCiAgICAgICAgY29scyA9IHN0YXJ0c193aXRoKCJwY3RfViIpLA0KICAgICAgICBuYW1lc190byA9ICJ2ZWxvY2l0eV9iYW5kIiwNCiAgICAgICAgdmFsdWVzX3RvID0gInBlcmNlbnRfZWZmb3J0Ig0KICAgICAgKSAlPiUNCiAgICAgIG11dGF0ZSgNCiAgICAgICAgdmVsb2NpdHlfYmFuZCA9IGZhY3RvcigNCiAgICAgICAgICB2ZWxvY2l0eV9iYW5kLA0KICAgICAgICAgIGxldmVscyA9IHBhc3RlMCgicGN0X1YiLCA4OjIpLA0KICAgICAgICAgIGxhYmVscyA9IHBhc3RlMCgiViIsIDg6MikNCiAgICAgICAgKQ0KICAgICAgKQ0KICAgIA0KICAgICMgU2tpcCBlbXB0eSBkYXRhDQogICAgaWYgKG5yb3cocGxheWVyX3dlZWtseV9kYXRhKSA9PSAwKSByZXR1cm4oTlVMTCkNCiAgICANCiAgICAjIEdlbmVyYXRlIGFuZCBwcmludCB0aGUgcGxvdA0KICAgIHBsb3QgPC0gZ2dwbG90KHBsYXllcl93ZWVrbHlfZGF0YSwgYWVzKHggPSB3ZWVrKSkgKw0KICAgICAgZ2VvbV9jb2woYWVzKHkgPSBwZXJjZW50X2VmZm9ydCwgZmlsbCA9IHZlbG9jaXR5X2JhbmQpLCBwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgICAgIGdlb21fbGluZShhZXMoeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHksIGdyb3VwID0gMSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEuMikgKw0KICAgICAgZ2VvbV9wb2ludChhZXMoeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHkpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSArDQogICAgICBzY2FsZV9maWxsX21hbnVhbCgNCiAgICAgICAgdmFsdWVzID0gYygNCiAgICAgICAgICAiVjIiID0gImRhcmtncmVlbiIsDQogICAgICAgICAgIlYzIiA9ICJncmVlbjIiLA0KICAgICAgICAgICJWNCIgPSAiZ3JlZW55ZWxsb3ciLA0KICAgICAgICAgICJWNSIgPSAieWVsbG93IiwNCiAgICAgICAgICAiVjYiID0gIm9yYW5nZSIsDQogICAgICAgICAgIlY3IiA9ICJ0b21hdG8iLA0KICAgICAgICAgICJWOCIgPSAiZmlyZWJyaWNrIg0KICApDQopICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgICAgIG5hbWUgPSAiQmFuZCBEaXN0cmlidXRpb24gKCUpIiwNCiAgICAgICAgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4sIG5hbWUgPSAiV2Vla2x5IE1heCBWZWxvY2l0eSAoJSBvZiBQUikiKQ0KICAgICAgKSArDQogICAgICBsYWJzKA0KICAgICAgICB0aXRsZSA9IHBhc3RlKCJWZWxvY2l0eSBCYW5kIEVmZm9ydCAlIGFuZCBTcGVlZCBUcmVuZCBmb3IgUGxheWVyIiwgcGxheWVyX2lkKSwNCiAgICAgICAgeCA9ICJXZWVrIiwNCiAgICAgICAgZmlsbCA9ICJWZWxvY2l0eSBCYW5kIg0KICAgICAgKSArDQogICAgICB0aGVtZV9jbGFzc2ljKCkNCiAgICANCiAgICBwcmludChwbG90KSAgIyBEaXNwbGF5IHBsb3QNCiAgICANCiAgICByZXR1cm4oTlVMTCkNCiAgfSkNCmBgYA0KDQpFYWNoIHBsYXllciBpcyBkaWZmZXJlbnQgaW4gcmVhY2hpbmcgZGlmZmVyZW50IHZlbG9jaXR5IGJhbmRzIGluIHJlbGF0aW9uIHRvIHRoZWlyIHRvcCBzcGVlZHMuIFNvbWUgcGxheWVycywgSURfMTEgZm9yIGV4YW1wbGUsIHJhcmVseSBlbnRlciBiYW5kcyA2IG9yIGhpZ2hlciwgZXZlbiBpZiB0aGV5IGFyZSBjbG9zZSB0byAxMDAlIG9mIHRoZWlyIG1heCB2ZWxvY2l0eS4gVGhpcyBzaG93cyB0aGUgaXNzdWVzIHdpdGggdXNpbmcgYWJzb2x1dGUgYmFuZHMgZm9yIHRoZSB3aG9sZSB0ZWFtLg0KDQpgYGB7cn0NCiMgUGljayBvbmUgcGxheWVyDQpwbGF5ZXJfaWQgPC0gIklEXzkzIg0KDQojIEZpbHRlciBkYXRhIGZvciB0aGF0IHBsYXllciBhbmQgd2Vla3MNCnBsYXllcl9kYXRhIDwtIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCA9PSBwbGF5ZXJfaWQpDQoNCiMgUGxvdCBvZiBUb3RhbCBTcHJpbnRpbmcgRWZmb3J0cyBwZXIgV2Vlaw0KZ2dwbG90KHBsYXllcl9kYXRhLCBhZXMoeCA9IHdlZWssIHkgPSB0b3RhbF9lZmZvcnRzKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiI0NGQjg3QyIsIHNpemUgPSAxKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiI0NGQjg3QyIsIHNpemUgPSAyKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSBwYXN0ZSgiVG90YWwgU3ByaW50aW5nIEVmZm9ydHMgcGVyIFdlZWsgZm9yIFBsYXllciIsIHBsYXllcl9pZCksDQogICAgeCA9ICJXZWVrIiwNCiAgICB5ID0gIlRvdGFsIFNwcmludGluZyBFZmZvcnRzIg0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiNQbG90IG9mIFNwcmludCBFZmZvcnQgRGlzdHJpYnV0aW9uIGJ5IFZlbG9jaXR5IEJhbmQNCg0KIyBSZXNoYXBlIGFic29sdXRlIGNvdW50cyB0byBsb25nIGZvcm1hdA0KcGxheWVyX2NvdW50c19sb25nIDwtIHBsYXllcl9kYXRhICU+JQ0KICBzZWxlY3Qod2VlaywgVjIsIFYzLCBWNCwgVjUsIFY2LCBWNywgVjgpICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IFYyOlY4LA0KICAgIG5hbWVzX3RvID0gInZlbG9jaXR5X2JhbmQiLA0KICAgIHZhbHVlc190byA9ICJlZmZvcnRfY291bnQiDQogICkgJT4lDQogIG11dGF0ZSgNCiAgICB2ZWxvY2l0eV9iYW5kID0gZmFjdG9yKHZlbG9jaXR5X2JhbmQsIGxldmVscyA9IHBhc3RlMCgiViIsIDg6MikpDQogICkNCg0KZ2dwbG90KHBsYXllcl9jb3VudHNfbG9uZywgYWVzKHggPSB3ZWVrLCB5ID0gZWZmb3J0X2NvdW50LCBmaWxsID0gdmVsb2NpdHlfYmFuZCkpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAic3RhY2siKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9IHBhc3RlKCJTcHJpbnQgRWZmb3J0IERpc3RyaWJ1dGlvbiBieSBWZWxvY2l0eSBCYW5kIGZvciBQbGF5ZXIiLCBwbGF5ZXJfaWQpLA0KICAgIHggPSAiV2VlayIsDQogICAgeSA9ICJTcHJpbnQgRWZmb3J0IENvdW50IiwNCiAgICBmaWxsID0gIlZlbG9jaXR5IEJhbmQiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KIyMjIyBFeHBsb3JpbmcgY29ycmVsYXRpb25zIGJldHdlZW4gYmFuZHMgYW5kIG1heCBzcGVlZHMNCg0KYGBge3J9DQoNCg0KIyBTZWxlY3QgcmVsZXZhbnQgY29sdW1ucw0KY29yX2RhdGEgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIHNlbGVjdChwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCBwY3RfVjIsIHBjdF9WMywgcGN0X1Y0LCBwY3RfVjUsIHBjdF9WNiwgcGN0X1Y3LCBwY3RfVjgpDQoNCiMgQ29tcHV0ZSBjb3JyZWxhdGlvbiBtYXRyaXgNCmNvcl9tYXRyaXggPC0gY29yKGNvcl9kYXRhLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikNCg0KY29yX21hdHJpeA0KYGBgDQpWNCBhbmQgVjUgYXBwZWFyIHRvIGJlIHNsaWdodGx5IG1vcmUgcHJlZGljdGl2ZSBvZiB0b3Atc3BlZWQgYWNoaWV2ZW1lbnQgdGhhbiBWNiwgVjcgb3IgVjgg4oCUIHBvc3NpYmx5IGJlY2F1c2UgdGhleSdyZSBtb3JlIGZyZXF1ZW50bHkgcmVhY2hlZCB6b25lcy4NCg0KVjIgaGFzIGEgbW9kZXJhdGVseSBzdHJvbmcgbmVnYXRpdmUgbGluZWFyIHJlbGF0aW9uc2hpcCB3aXRoIHJlYWNoaW5nIHRvcC1zcGVlZC4gV2hlbiBhdGhsZXRlcyBzcGVuZCBtb3JlIGVmZm9ydCBpbiB0aGlzIGxvdy1zcGVlZCBiYW5kLCB0aGVpciB3ZWVrbHkgdG9wIHNwZWVkIChhcyAlIG9mIG1heCkgdGVuZHMgdG8gYmUgbG93ZXIuDQoNCmBgYHtyfQ0KIyBMaW5lYXIgbW9kZWwgd2l0aCBhbGwgYmFuZHMgcHJlZGljdGluZyBwY3Rfb2ZfbWF4X3ZlbG9jaXR5DQptb2RlbF9hbGxfYmFuZHMgPC0gbG0oDQogIHBjdF9vZl9tYXhfdmVsb2NpdHkgfiBwY3RfVjIgKyBwY3RfVjMgKyBwY3RfVjQgKyBwY3RfVjUgKyBwY3RfVjYgKyBwY3RfVjcgKyBwY3RfVjgsDQogIGRhdGEgPSB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cw0KKQ0Kc3VtbWFyeShtb2RlbF9hbGxfYmFuZHMpDQpgYGANCmBgYHtyfQ0KIyBDb21wdXRlIGNoYW5nZSBpbiB0b3Agc3BlZWRzIHdlZWstdG8td2Vlaw0Kd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIGFycmFuZ2UoYW5vbl9pZCwgd2VlaykgJT4lDQogIGdyb3VwX2J5KGFub25faWQpICU+JQ0KICBtdXRhdGUoDQogICAgcGN0X29mX21heF92ZWxvY2l0eV9jaGFuZ2UgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5IC0gbGFnKHBjdF9vZl9tYXhfdmVsb2NpdHkpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTY2F0dGVycGxvdCB3aXRoIHNtb290aGluZyBsaW5lIGZvciBWOCBiYW5kIGVmZm9ydA0KDQojIENyZWF0ZSBWOCBkYXRhIHNldCB0aGF0IG9ubHkgaW5jbHVkZXMgd2hlbiBzb21lb25lIHdhcyBpbiBWOCBmb3IgYSBnaXZlbiB3ZWVrDQpWOCA8LSB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyAlPiUNCiAgZmlsdGVyKHBjdF9WOCA+IDApDQoNCiMgUGxvdA0KZ2dwbG90KFY4LCBhZXMoeCA9IHBjdF9WOCwgeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHlfY2hhbmdlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC40KSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gVFJVRSwgY29sb3IgPSAiZGFya3JlZCIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJDaGFuZ2UgaW4gTWF4IFNwZWVkIHZzLiBWOCBCYW5kIFVzYWdlIiwNCiAgICB4ID0gIlBlcmNlbnQgb2YgVG90YWwgRWZmb3J0IGluIFY4IEJhbmQgKCUpIiwNCiAgICB5ID0gIkNoYW5nZSBpbiBNYXggU3BlZWQgZnJvbSBQcmlvciBXZWVrICglKSINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCmBgYHtyfQ0Kd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIGdyb3VwX2J5KGFub25faWQpICU+JQ0KICBtdXRhdGUoDQogICAgbGFnX1YyID0gbGFnKFYyKSwNCiAgICBsYWdfVjMgPSBsYWcoVjMpLA0KICAgIGxhZ19WNCA9IGxhZyhWNCksDQogICAgbGFnX1Y1ID0gbGFnKFY1KSwNCiAgICBsYWdfVjYgPSBsYWcoVjYpLA0KICAgIGxhZ19WNyA9IGxhZyhWNyksDQogICAgbGFnX1Y4ID0gbGFnKFY4KSwNCiAgICANCiAgICBsYWdfcGN0X1YyID0gbGFnKHBjdF9WMiksDQogICAgbGFnX3BjdF9WMyA9IGxhZyhwY3RfVjMpLA0KICAgIGxhZ19wY3RfVjQgPSBsYWcocGN0X1Y0KSwNCiAgICBsYWdfcGN0X1Y1ID0gbGFnKHBjdF9WNSksDQogICAgbGFnX3BjdF9WNiA9IGxhZyhwY3RfVjYpLA0KICAgIGxhZ19wY3RfVjcgPSBsYWcocGN0X1Y3KSwNCiAgICBsYWdfcGN0X1Y4ID0gbGFnKHBjdF9WOCkNCiAgKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3J9DQojIE1heWJlIGdyb3VwIGJ5IHBvc2l0aW9uDQoNCiMgR2V0IFBsYXllciBwb3NpdGlvbnMNCnBsYXllcl9wb3NpdGlvbnMgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgc2VsZWN0KGFub25faWQsIFByaW1hcnkuUG9zaXRpb24pICU+JQ0KICBkaXN0aW5jdCgpDQoNCiMgSm9pbiBQb3NpdGlvbiBpbnRvIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzDQp3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyA8LSB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyAlPiUNCiAgbGVmdF9qb2luKHBsYXllcl9wb3NpdGlvbnMsIGJ5ID0gImFub25faWQiKQ0KDQojIFBvc2l0aW9uIGdyb3Vwcw0Kd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHMgJT4lDQogIG11dGF0ZSgNCiAgICBQb3NpdGlvbl9Hcm91cCA9IGNhc2Vfd2hlbigNCiAgICAgIFByaW1hcnkuUG9zaXRpb24gJWluJSBjKCJRQiIsICJMQiIsICJURSIsICJSQiIpIH4gIkNPTUJPIiwNCiAgICAgIFByaW1hcnkuUG9zaXRpb24gJWluJSBjKCJPTCIsICJETCIsICJERSIpIH4gIkJJR1MiLA0KICAgICAgUHJpbWFyeS5Qb3NpdGlvbiAlaW4lIGMoIldSIiwgIkRCIiwgIkRCLCBXUiIpIH4gIlNLSUxMIiwNCiAgICAgIFRSVUUgfiAiT1RIRVIiDQogICAgKQ0KICApICU+JQ0KICBmaWx0ZXIoUG9zaXRpb25fR3JvdXAgIT0gIk9USEVSIikNCg0KZ2dwbG90KHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzLCBhZXMoeCA9IFBvc2l0aW9uX0dyb3VwLCB5ID0gcGN0X29mX21heF92ZWxvY2l0eV9jaGFuZ2UpKSArDQogIGdlb21fYm94cGxvdChmaWxsID0gInNreWJsdWUiLCBvdXRsaWVyLmNvbG9yID0gInJlZCIsIG91dGxpZXIuc2l6ZSA9IDEuNSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIldlZWtseSBDaGFuZ2UgaW4gJSBvZiBNYXggVmVsb2NpdHkgYnkgUG9zaXRpb24iLA0KICAgIHggPSAiUG9zaXRpb24iLA0KICAgIHkgPSAiJSBDaGFuZ2UgaW4gTWF4IFZlbG9jaXR5Ig0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQpgYGB7cn0NCndlZWtseV9iYW5kX2VmZm9ydF9ieV9ncm91cCA8LSB3ZWVrbHlfdmVsb2NpdHlfZWZmb3J0cyAlPiUNCiAgZ3JvdXBfYnkod2VlaywgUG9zaXRpb25fR3JvdXApICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVhbl9WMiA9IG1lYW4oVjIsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9WMyA9IG1lYW4oVjMsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9WNCA9IG1lYW4oVjQsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9WNSA9IG1lYW4oVjUsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9WNiA9IG1lYW4oVjYsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9WNyA9IG1lYW4oVjcsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9WOCA9IG1lYW4oVjgsIG5hLnJtID0gVFJVRSksDQogICAgDQogICAgbWVhbl9sYWdfVjIgPSBtZWFuKGxhZ19WMiwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX2xhZ19WMyA9IG1lYW4obGFnX1YzLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fbGFnX1Y0ID0gbWVhbihsYWdfVjQsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9sYWdfVjUgPSBtZWFuKGxhZ19WNSwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX2xhZ19WNiA9IG1lYW4obGFnX1Y2LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fbGFnX1Y3ID0gbWVhbihsYWdfVjcsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9sYWdfVjggPSBtZWFuKGxhZ19WOCwgbmEucm0gPSBUUlVFKSwNCiAgICANCiAgICBtZWFuX3BjdF9WMiA9IG1lYW4ocGN0X1YyLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fcGN0X1YzID0gbWVhbihwY3RfVjMsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9wY3RfVjQgPSBtZWFuKHBjdF9WNCwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX3BjdF9WNSA9IG1lYW4ocGN0X1Y1LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fcGN0X1Y2ID0gbWVhbihwY3RfVjYsIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9wY3RfVjcgPSBtZWFuKHBjdF9WNywgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX3BjdF9WOCA9IG1lYW4ocGN0X1Y4LCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSA9IG1lYW4oV2Vla2x5X01heF9WZWxvY2l0eSwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWFuX3BjdF9tYXhfdmVsb2NpdHkgPSBtZWFuKHBjdF9vZl9tYXhfdmVsb2NpdHksIG5hLnJtID0gVFJVRSksDQogICAgbWVhbl9wY3RfbWF4X3ZlbG9jaXR5X2NoYW5nZSA9IG1lYW4ocGN0X29mX21heF92ZWxvY2l0eV9jaGFuZ2UsIG5hLnJtID0gVFJVRSksDQogICAgbl9wbGF5ZXJzID0gbigpDQogICkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCg0KYGBge3J9DQoNCiMgUGl2b3QgbG9uZ2VyIHRvIGdldCBiYW5kcyBpbiBvbmUgY29sdW1uIGZvciBlYXNpZXIgcGxvdHRpbmcNCmJhbmRfbG9uZyA8LSB3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAgJT4lDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gc3RhcnRzX3dpdGgoIm1lYW5fcGN0X1YiKSwNCiAgICBuYW1lc190byA9ICJ2ZWxvY2l0eV9iYW5kIiwNCiAgICB2YWx1ZXNfdG8gPSAibWVhbl9wY3RfZWZmb3J0Ig0KICApICU+JQ0KICBtdXRhdGUoDQogICAgdmVsb2NpdHlfYmFuZCA9IGZhY3Rvcih2ZWxvY2l0eV9iYW5kLCBsZXZlbHMgPSBwYXN0ZTAoIm1lYW5fcGN0X1YiLCAyOjgpKQ0KICApDQoNCmdncGxvdChiYW5kX2xvbmcsIGFlcyh4ID0gd2VlaywgeSA9IG1lYW5fcGN0X2VmZm9ydCwgY29sb3IgPSB2ZWxvY2l0eV9iYW5kKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsNCiAgZmFjZXRfd3JhcCh+IFBvc2l0aW9uX0dyb3VwKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiV2Vla2x5IE1lYW4gJSBFZmZvcnQgaW4gVmVsb2NpdHkgQmFuZHMgYnkgUG9zaXRpb24gR3JvdXAiLA0KICAgIHggPSAiV2VlayIsDQogICAgeSA9ICJNZWFuICUgRWZmb3J0IiwNCiAgICBjb2xvciA9ICJWZWxvY2l0eSBCYW5kIg0KICApICsNCiAgIHNjYWxlX2NvbG9yX21hbnVhbCgNCiAgICAgICAgdmFsdWVzID0gYygNCiAgICAgICAgICAibWVhbl9wY3RfVjIiID0gImRhcmtncmVlbiIsDQogICAgICAgICAgIm1lYW5fcGN0X1YzIiA9ICJncmVlbjIiLA0KICAgICAgICAgICJtZWFuX3BjdF9WNCIgPSAiZ3JlZW55ZWxsb3ciLA0KICAgICAgICAgICJtZWFuX3BjdF9WNSIgPSAieWVsbG93IiwNCiAgICAgICAgICAibWVhbl9wY3RfVjYiID0gIm9yYW5nZSIsDQogICAgICAgICAgIm1lYW5fcGN0X1Y3IiA9ICJ0b21hdG8iLA0KICAgICAgICAgICJtZWFuX3BjdF9WOCIgPSAiZmlyZWJyaWNrIg0KICApDQopICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQpUaGlzIHNob3dzIHRoZSBtZWFuIHBlcmNlbnQgb2YgdG90YWwgZWZmb3J0cyBpbiBlYWNoIGJhbmQgb3ZlciB3ZWVrcyBieSBlYWNoIHBvc2l0aW9uIGdyb3VwLg0KDQpgYGB7cn0NCiMgUGxvdCBtZWFuIHdlZWtseSBtYXggdmVsb2NpdHkNCmdncGxvdCh3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAsIGFlcyh4ID0gd2VlaywgeSA9IG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSwgY29sb3IgPSBQb3NpdGlvbl9Hcm91cCkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJNZWFuIFdlZWtseSBNYXggVmVsb2NpdHkgYnkgUG9zaXRpb24gR3JvdXAiLA0KICAgIHggPSAiV2VlayIsDQogICAgeSA9ICJNZWFuIFdlZWtseSBNYXggVmVsb2NpdHkiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KDQojIFBsb3QgbWVhbiAlIG1heCB2ZWxvY2l0eSBjaGFuZ2UNCmdncGxvdCh3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAsIGFlcyh4ID0gd2VlaywgeSA9IG1lYW5fcGN0X21heF92ZWxvY2l0eV9jaGFuZ2UsIGNvbG9yID0gUG9zaXRpb25fR3JvdXApKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTWVhbiAlIENoYW5nZSBpbiBNYXggVmVsb2NpdHkgYnkgUG9zaXRpb24gR3JvdXAiLA0KICAgIHggPSAiV2VlayIsDQogICAgeSA9ICJNZWFuICUgQ2hhbmdlIGluIE1heCBWZWxvY2l0eSINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICAjIFBsb3QgdGhlIHN0YWNrZWQgbGluZXMgZm9yIGJhbmQgZWZmb3J0ICUNCiAgZ2VvbV9saW5lKGRhdGEgPSBiYW5kX2xvbmcsIGFlcyh4ID0gd2VlaywgeSA9IG1lYW5fcGN0X2VmZm9ydCwgY29sb3IgPSB2ZWxvY2l0eV9iYW5kKSwgc2l6ZSA9IDEpICsNCiAgZ2VvbV9saW5lKGRhdGEgPSB3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAsDQogICAgICAgICAgICBhZXMoeCA9IHdlZWssIHkgPSBtZWFuX3dlZWtseV9tYXhfdmVsb2NpdHkpLA0KICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMS4yKSArDQoNCiAgZmFjZXRfd3JhcCh+IFBvc2l0aW9uX0dyb3VwKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiV2Vla2x5IE1lYW4gJSBFZmZvcnQgaW4gVmVsb2NpdHkgQmFuZHMgYnkgUG9zaXRpb24gR3JvdXBcbndpdGggTWVhbiBXZWVrbHkgTWF4IFZlbG9jaXR5IiwNCiAgICB4ID0gIldlZWsiLA0KICAgIHkgPSAiTWVhbiAlIEVmZm9ydCIsDQogICAgY29sb3IgPSAiVmVsb2NpdHkgQmFuZCINCiAgKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgNCiAgICAgICAgdmFsdWVzID0gYygNCiAgICAgICAgICAibWVhbl9wY3RfVjIiID0gImRhcmtncmVlbiIsDQogICAgICAgICAgIm1lYW5fcGN0X1YzIiA9ICJncmVlbjIiLA0KICAgICAgICAgICJtZWFuX3BjdF9WNCIgPSAiZ3JlZW55ZWxsb3ciLA0KICAgICAgICAgICJtZWFuX3BjdF9WNSIgPSAieWVsbG93IiwNCiAgICAgICAgICAibWVhbl9wY3RfVjYiID0gIm9yYW5nZSIsDQogICAgICAgICAgIm1lYW5fcGN0X1Y3IiA9ICJ0b21hdG8iLA0KICAgICAgICAgICJtZWFuX3BjdF9WOCIgPSAiZmlyZWJyaWNrIg0KICApDQopICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQpgYGB7cn0NCiMgRGVmaW5lIG1heCBlZmZvcnQgYW5kIG1heCB2ZWxvY2l0eSByYW5nZSAoYWRqdXN0IGFzIG5lZWRlZCkNCm1heF9lZmZvcnQgPC0gNjcNCm1heF92ZWxvY2l0eSA8LSBtYXgod2Vla2x5X2JhbmRfZWZmb3J0X2J5X2dyb3VwJG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSwgbmEucm0gPSBUUlVFKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fbGluZShkYXRhID0gYmFuZF9sb25nLCBhZXMoeCA9IHdlZWssIHkgPSBtZWFuX3BjdF9lZmZvcnQsIGNvbG9yID0gdmVsb2NpdHlfYmFuZCksIHNpemUgPSAxKSArDQogIA0KICAjIFNjYWxlIG1heF92ZWxvY2l0eSB0byAlIGVmZm9ydCBzY2FsZSB0byBvdmVybGF5IG9uIGxlZnQgYXhpcw0KICBnZW9tX2xpbmUoZGF0YSA9IHdlZWtseV9iYW5kX2VmZm9ydF9ieV9ncm91cCwgDQogICAgICAgICAgICBhZXMoeCA9IHdlZWssIHkgPSAobWVhbl93ZWVrbHlfbWF4X3ZlbG9jaXR5IC8gbWF4X3ZlbG9jaXR5KSAqIG1heF9lZmZvcnQsIGNvbG9yID0gIk1heCBWZWxvY2l0eSIpLA0KICAgICAgICAgICAgc2l6ZSA9IDEuMikgKw0KICANCiAgZmFjZXRfd3JhcCh+IFBvc2l0aW9uX0dyb3VwKSArDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBuYW1lID0gIk1lYW4gJSBFZmZvcnQiLA0KICAgIHNlYy5heGlzID0gc2VjX2F4aXMofiAuICogbWF4X3ZlbG9jaXR5IC8gbWF4X2VmZm9ydCwgbmFtZSA9ICJNZWFuIFdlZWtseSBNYXggVmVsb2NpdHkgKG0vcykiKSAgIyBhZGp1c3QgdW5pdCBpZiBuZWVkZWQNCiAgKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKA0KICAgICAgIlYyIiA9ICJkYXJrZ3JlZW4iLA0KICAgICAgIlYzIiA9ICJncmVlbjIiLA0KICAgICAgIlY0IiA9ICJncmVlbnllbGxvdyIsDQogICAgICAiVjUiID0gInllbGxvdyIsDQogICAgICAiVjYiID0gIm9yYW5nZSIsDQogICAgICAiVjciID0gInRvbWF0byIsDQogICAgICAiVjgiID0gImZpcmVicmljayIsDQogICAgICAiTWF4IFZlbG9jaXR5IiA9ICJibGFjayINCiAgICApDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIldlZWtseSBNZWFuICUgRWZmb3J0IGluIFZlbG9jaXR5IEJhbmRzIGJ5IFBvc2l0aW9uIEdyb3VwXG53aXRoIE1lYW4gV2Vla2x5IE1heCBWZWxvY2l0eSIsDQogICAgeCA9ICJXZWVrIiwNCiAgICBjb2xvciA9ICJWZWxvY2l0eSBCYW5kIg0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQoNCg0KYGBge3J9DQojIE1vZGVsaW5nDQoNCiMgU2VwYXJhdGUgYnkgcG9zaXRpb24gZ3JvdXANCmJpZ3MgPC0gd2Vla2x5X2JhbmRfZWZmb3J0X2J5X2dyb3VwICU+JQ0KICBmaWx0ZXIoUG9zaXRpb25fR3JvdXAgPT0gIkJJR1MiKQ0KDQpza2lsbCA8LSB3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAgJT4lDQogIGZpbHRlcihQb3NpdGlvbl9Hcm91cCA9PSAiU0tJTEwiKQ0KDQpjb21ibyA8LSB3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAgJT4lDQogIGZpbHRlcihQb3NpdGlvbl9Hcm91cCA9PSAiQ09NQk8iKQ0KDQojIEJpZ3MgTW9kZWwNCm1vZGVsX2JpZ3MgPC0gbG0oDQogIG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSB+IG1lYW5fbGFnX1YyICsgbWVhbl9sYWdfVjMgKyBtZWFuX2xhZ19WNCArIG1lYW5fbGFnX1Y1ICsgbWVhbl9sYWdfVjYgKyBtZWFuX2xhZ19WNyArIG1lYW5fbGFnX1Y4LCBkYXRhID0gYmlncw0KICApDQpzdW1tYXJ5KG1vZGVsX2JpZ3MpDQoNCiMgU2tpbGwgTW9kZWwNCm1vZGVsX3NraWxsIDwtIGxtKA0KICBtZWFuX3dlZWtseV9tYXhfdmVsb2NpdHkgfiBtZWFuX2xhZ19WMiArIG1lYW5fbGFnX1YzICsgbWVhbl9sYWdfVjQgKyBtZWFuX2xhZ19WNSArIG1lYW5fbGFnX1Y2ICsgbWVhbl9sYWdfVjcgKyBtZWFuX2xhZ19WOCwgZGF0YSA9IHNraWxsDQogICkNCnN1bW1hcnkobW9kZWxfc2tpbGwpDQoNCiMgQ29tYm8gTW9kZWwNCm1vZGVsX2NvbWJvIDwtIGxtKA0KICBtZWFuX3dlZWtseV9tYXhfdmVsb2NpdHkgfiBtZWFuX2xhZ19WMiArIG1lYW5fbGFnX1YzICsgbWVhbl9sYWdfVjQgKyBtZWFuX2xhZ19WNSArIG1lYW5fbGFnX1Y2ICsgbWVhbl9sYWdfVjcgKyBtZWFuX2xhZ19WOCwgZGF0YSA9IGNvbWJvKQ0Kc3VtbWFyeShtb2RlbF9jb21ibykNCmBgYA0KDQpgYGB7cn0NCiMgR3JvdXAgY29ycmVsYXRpb246DQoNCiMgU2VsZWN0IHJlbGV2YW50IHZhcmlhYmxlcw0KdmFycyA8LSBjKCJtZWFuX3dlZWtseV9tYXhfdmVsb2NpdHkiLCAibWVhbl9sYWdfVjIiLCAibWVhbl9sYWdfVjMiLCANCiAgICAgICAgICAibWVhbl9sYWdfVjQiLCAibWVhbl9sYWdfVjUiLCAibWVhbl9sYWdfVjYiLCAibWVhbl9sYWdfVjciLCAibWVhbl9sYWdfVjgiKQ0KDQojIENvcnJlbGF0aW9uIG1hdHJpY2VzDQpjb3JfYmlncyA8LSBjb3IoYmlnc1ssIHZhcnNdLCB1c2UgPSAiY29tcGxldGUub2JzIikNCmNvcl9za2lsbCA8LSBjb3Ioc2tpbGxbLCB2YXJzXSwgdXNlID0gImNvbXBsZXRlLm9icyIpDQpjb3JfY29tYm8gPC0gY29yKGNvbWJvWywgdmFyc10sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0Kcm91bmQoY29yX2JpZ3MsIDIpDQpyb3VuZChjb3Jfc2tpbGwsIDIpDQpyb3VuZChjb3JfY29tYm8sIDIpDQpgYGANCg0KDQpgYGB7cn0NCiMgUmVkdWNlIGZvciBiZXR0ZXIgbW9kZWxpbmcNCmJpZ3MgPC0gYmlncyAlPiUNCiAgbXV0YXRlKA0KICAgIGxvd19iYW5kID0gKG1lYW5fbGFnX1YyICsgbWVhbl9sYWdfVjMpIC8gMiwNCiAgICBtaWRfYmFuZCA9IChtZWFuX2xhZ19WNCArIG1lYW5fbGFnX1Y1ICsgbWVhbl9sYWdfVjYpIC8gMywNCiAgICBoaWdoX2JhbmQgPSAobWVhbl9sYWdfVjcgKyBtZWFuX2xhZ19WOCkgLyAyDQogICkNCm1vZGVsX2JpZ3Nfc2ltcGxlIDwtIGxtKG1lYW5fd2Vla2x5X21heF92ZWxvY2l0eSB+IGxvd19iYW5kICsgbWlkX2JhbmQgKyBoaWdoX2JhbmQsIGRhdGEgPSBiaWdzKQ0Kc3VtbWFyeShtb2RlbF9iaWdzX3NpbXBsZSkNCg0KDQojIFNraWxsIEdyb3VwDQpza2lsbCA8LSBza2lsbCAlPiUNCiAgbXV0YXRlKA0KICAgIGxvd19iYW5kID0gKG1lYW5fbGFnX1YyICsgbWVhbl9sYWdfVjMgKyBtZWFuX2xhZ19WNCkgLyAzLA0KICAgIG1pZF9iYW5kID0gKG1lYW5fbGFnX1Y1ICsgbWVhbl9sYWdfVjYpIC8gMiwNCiAgICBoaWdoX2JhbmQgPSAobWVhbl9sYWdfVjcgKyBtZWFuX2xhZ19WOCkgLyAyDQogICkNCm1vZGVsX3NraWxsX3NpbXBsZSA8LSBsbShtZWFuX3dlZWtseV9tYXhfdmVsb2NpdHkgfiBsb3dfYmFuZCArIG1pZF9iYW5kICsgaGlnaF9iYW5kLCBkYXRhID0gc2tpbGwpDQpzdW1tYXJ5KG1vZGVsX3NraWxsX3NpbXBsZSkNCg0KIyBDb21ibyBncm91cA0KY29tYm8gPC0gY29tYm8gJT4lDQogIG11dGF0ZSgNCiAgICBsb3dfYmFuZCA9IChtZWFuX2xhZ19WMiArIG1lYW5fbGFnX1YzKSAvIDIsDQogICAgbWlkX2JhbmQgPSAobWVhbl9sYWdfVjQgKyBtZWFuX2xhZ19WNSkgLyAyLA0KICAgIGhpZ2hfYmFuZCA9IChtZWFuX2xhZ19WNiArIG1lYW5fbGFnX1Y3ICsgbWVhbl9sYWdfVjgpIC8gMw0KICApDQptb2RlbF9jb21ib19zaW1wbGUgPC0gbG0obWVhbl93ZWVrbHlfbWF4X3ZlbG9jaXR5IH4gbG93X2JhbmQgKyBtaWRfYmFuZCArIGhpZ2hfYmFuZCwgZGF0YSA9IGNvbWJvKQ0Kc3VtbWFyeShtb2RlbF9jb21ib19zaW1wbGUpDQpgYGANCkJpZ3MgbW9kZWw6DQogIFN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgb3ZlcmFsbCBhbmQgZXhwbGFpbnMgYXJvdW5kIDMzJSBvZiB0aGUgdmFyaWF0aW9uIG9mIG1heCB2ZWxvY2l0eS4gVGhlIGxvdyBiYW5kIGlzIHNpZ25pZmljYW50bHkgbmVnYXRpdmUuIEV2ZXJ5IHVuaXQgaW5jcmVhc2UgaW4gdGhlIGxvdyBiYW5kIHJlc3VsdHMgaW4gYSBkZWNyZWFzZSBpbiBtYXggdmVsb2NpdHkuIFRoZSBtaWQgYW5kIGhpZ2ggYmFuZHMgYXJlIG5vdCBzaWduaWZpY2FudCBidXQgdGhlIGhpZ2ggYmFuZCBzaG93cyBhIHBvc2l0aXZlIHRyZW5kLg0KICBCYXNlZCBvbiB0aGlzIG1vZGVsLCBmb3IgYmlncywgbW9yZSBsb3ctYmFuZCBlZmZvcnQgbWF5IHJlZHVjZSB3ZWVrbHkgdG9wIHNwZWVkLCBwb3NzaWJseSBpbmRpY2F0aW5nIHVuZGVyZXhwb3N1cmUgb2YgaGlnaCBzcGVlZHMuIEhpZ2gtYmFuZCBlZmZvcnRzIG1pZ2h0IGhlbHAsIGJ1dCBhcmVu4oCZdCBjbGVhcmx5IGltcGFjdGZ1bCBpbiB0aGlzIGdyb3VwLg0KDQpTa2lsbCBtb2RlbDoNCiAgQWxtb3N0IHNpZ25pZmljYW50LiBMb3cgYmFuZCBoYXMgYSBuZWdhdGl2ZSBlZmZlY3QsIG1pZCBiYW5kIGhhcyBhIHBvc2l0aXZlIGVmZmVjdCwgYW5kIGhpZ2ggYmFuZCBpcyBub3Qgc2lnbmlmaWNhbnQuIA0KICBCYXNlZCBvbiB0aGlzIG1vZGVsLCBmb3Igc2tpbGwgcGxheWVycywgbW9yZSBsb3ctaW50ZW5zaXR5IGV4cG9zdXJlIHNlZW1zIGRldHJpbWVudGFsIHRvIG1heCBzcGVlZCwgd2hpbGUgbW9kZXJhdGUgYmFuZCAoVjXigJNWNikgZXhwb3N1cmUgbWF5IGVuaGFuY2UgaXQuDQogIA0KQ29tYm8gbW9kZWw6DQogIE1vZGVsIGlzIG5vdCBzaWduaWZpY2FudC4gVGhlIGNvbWJvIHBvc2l0aW9uIGdyb3VwIGNvbnRhaW5zIG1hbnkgZGlmZmVyZW50IHR5cGUgb2YgcG9zaXRpb25zIChRQnMsIFJCcywgVEVzLCBMQnMpIHdpdGggZGlmZmVyZW50IG1vdmVtZW50IGFuZCB2ZWxvY2l0aWVzLiBUaGlzIHZhcmlhYmlsaXR5IG1heSBvYnNjdXJlIHJlbGF0aW9uc2hpcHMuDQogIA0KQWNyb3NzIGdyb3VwcywgbG93LWJhbmQgZXhwb3N1cmUgaXMgY29uc2lzdGVudGx5IG5lZ2F0aXZlbHkgcmVsYXRlZCB0byBwZWFrIHdlZWtseSBzcGVlZCDigJQgc3VnZ2VzdGluZyB0aGF0IHRvbyBtdWNoIGxvdy1pbnRlbnNpdHkgd29yayBtYXkgcmVkdWNlIHRoZSBjYXBhY2l0eSB0byByZWFjaCBoaWdoIHNwZWVkcy4NCg0KIyMgQXJlIHJlbGF0aXZlIGVmZm9ydHMgYW5kIGJhbmRzIG1vcmUgYWR2YW50YWdlb3VzIHRoYW4gdGhlIGFic29sdXRlIGJhbmRzIHByb3ZpZGVkPw0KDQpMb29rIGF0IElEXzExJ3MgcGxvdCBmcm9tIHRoZSBsYXN0IHF1ZXN0aW9uLiBUaGlzIHBsYXllciBpcyBhbiBvZmZlbnNpdmUgbGluZW1hbi4gTG9va2luZyBhdCB0aGUgcGxvdCwgaGUgaXMgbW9zdGx5IGluIGJhbmQgMiBhbmQgMyB3aGlsZSBuZXZlciByZWFjaGluZyBiYW5kcyA3IG9yIDggYW5kIHJhcmVseSByZWFjaGluZyBiYW5kIDYuIERlc3BpdGUgdGhpcywgdGhpcyBhdGhsZXRlJ3Mgd2Vla2x5IG1heCB2ZWxvY2l0eSBpcyBhbHdheXMgYXQgbGVhc3QgNzUlIG9mIHRoZWlyIGFsbC10aW1lIG1heCB2ZWxvY2l0eS4gT25seSBsb29raW5nIGF0IHRoZSBhYnNvbHV0ZSBiYW5kcyB0aGF0IGFyZSBwcm92aWRlZCwgd2UgbWlnaHQgY29tZSB0byB0aGUgY29uY2x1c2lvbiB0aGF0IElEXzExIGlzIG5vdCBhY2hpZXZpbmcgaGlnaCBydW5uaW5nIHNwZWVkcywgaG93ZXZlciBhZnRlciBsb29raW5nIGF0IGhpcyBtYXggdmVsb2NpdHkgZWZmb3J0cyByZWxhdGl2ZSB0byBoaXMgYWxsLXRpbWUgbWF4IHZlbG9jaXR5LCBoZSBpcyByZWFjaGluZyBoaWdoIHJ1bm5pbmcgc3BlZWRzLg0KDQojIyMjIENyZWF0ZSBSZWxhdGl2ZSBCYW5kcyBmb3IgSURfMTENCg0KYGBge3J9DQojIENyZWF0ZSBSZWxhdGl2ZSBCYW5kcyBmb3IgSURfMTENCg0KIyBQcmVwYXJlIElEXzExIGRhdGFzZXQgd2l0aCByZWxhdGl2ZSBiYW5kcw0KSURfMTEgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKERhdGUgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZSA8PSBhcy5EYXRlKCIyMDI1LTA3LTAxIikpICU+JSANCiAgZmlsdGVyKGFub25faWQgPT0gIklEXzExIiwgTWF4aW11bS5WZWxvY2l0eSAhPSAwKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShNYXhpbXVtLlZlbG9jaXR5KSkgJT4lDQogIG11dGF0ZSgNCiAgICB3ZWVrID0gZmxvb3JfZGF0ZShEYXRlLCB1bml0ID0gIndlZWsiKSwNCiAgICBwY3Rfb2ZfbWF4ID0gKE1heGltdW0uVmVsb2NpdHkgLyBQbGF5ZXIuTWF4LlZlbG9jaXR5KSAqIDEwMCwNCiAgICAjIENyZWF0ZSBSZWxhdGl2ZSBCYW5kcw0KICAgIHJlbGF0aXZlX2JhbmQgPSBjdXQoDQogICAgICBwY3Rfb2ZfbWF4LA0KICAgICAgYnJlYWtzID0gYygwLCA0MCwgNTAsIDYwLCA3MCwgODAsIDkwLCAxMDApLA0KICAgICAgbGFiZWxzID0gYygiVjIgKDw0MCUpIiwgIlYzICg0MC01MCUpIiwiVjQgKDUwLTYwJSkiICwiVjUgKDYwLTcwJSkiLCAiVjYgKDcwLTgwJSkiLCAiVjcgKDgwLTkwJSkiLCAiVjggKDkwLTEwMCUpIiksDQogICAgICByaWdodCA9IFRSVUUsICMgU2V0IHRvIFRSVUUgdG8gaW5jbHVkZSAxMDAlIGluIGJhbmQNCiAgICAgIGluY2x1ZGUubG93ZXN0ID0gVFJVRSAgIyBJbmNsdWRlcyAwIGluIGZpcnN0IGJhbmQNCiAgICApLA0KICAgICMgUmV2ZXJzZSBmYWN0b3IgbGV2ZWxzIHNvIGhpZ2hlc3QgYmFuZCBpcyBvbiB0b3AgaW4gcGxvdA0KICByZWxhdGl2ZV9iYW5kID0gZmFjdG9yKA0KICAgIHJlbGF0aXZlX2JhbmQsDQogICAgbGV2ZWxzID0gcmV2KGMoIlYyICg8NDAlKSIsICJWMyAoNDAtNTAlKSIsIlY0ICg1MC02MCUpIiAsIlY1ICg2MC03MCUpIiwgIlY2ICg3MC04MCUpIiwgIlY3ICg4MC05MCUpIiwgIlY4ICg5MC0xMDAlKSIpKQ0KICApDQogICkNCg0KIyBTdW1tYXJpemUgY291bnRzIHBlciB3ZWVrIGFuZCByZWxhdGl2ZSBiYW5kDQp3ZWVrbHlfZWZmb3J0IDwtIElEXzExICU+JQ0KICBncm91cF9ieSh3ZWVrLCByZWxhdGl2ZV9iYW5kKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgZ3JvdXBfYnkod2VlaykgJT4lDQogIG11dGF0ZShwY3RfZWZmb3J0ID0gKGNvdW50IC8gc3VtKGNvdW50KSkgKiAxMDApICU+JQ0KICB1bmdyb3VwKCkNCg0KIyBQaXZvdCB3aWRlciB0byBnZXQgcGVyY2VudGFnZXMgcGVyIGJhbmQgYXMgY29sdW1ucw0Kd2Vla2x5X2VmZm9ydF93aWRlIDwtIHdlZWtseV9lZmZvcnQgJT4lDQogIHNlbGVjdCh3ZWVrLCByZWxhdGl2ZV9iYW5kLCBwY3RfZWZmb3J0KSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHJlbGF0aXZlX2JhbmQsIHZhbHVlc19mcm9tID0gcGN0X2VmZm9ydCwgdmFsdWVzX2ZpbGwgPSAwKQ0KDQojIENhbGN1bGF0ZSB3ZWVrbHkgbWF4IHZlbG9jaXR5ICUgb2YgcGxheWVyIG1heCB2ZWxvY2l0eQ0Kd2Vla2x5X21heF92ZWxvY2l0eSA8LSBJRF8xMSAlPiUNCiAgZ3JvdXBfYnkod2VlaykgJT4lDQogIHN1bW1hcmlzZShwY3Rfb2ZfbWF4X3ZlbG9jaXR5ID0gbWF4KHBjdF9vZl9tYXgpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojIEpvaW4gdGhlIG1heCB2ZWxvY2l0eSAlIGJhY2sgdG8gd2lkZSBlZmZvcnQgZGF0YQ0Kd2Vla2x5X2VmZm9ydF93aWRlIDwtIHdlZWtseV9lZmZvcnRfd2lkZSAlPiUNCiAgbGVmdF9qb2luKHdlZWtseV9tYXhfdmVsb2NpdHksIGJ5ID0gIndlZWsiKQ0KDQojIFBpdm90IGxvbmdlciBmb3Igc3RhY2tlZCBiYXIgcGxvdHRpbmcNCndlZWtseV9lZmZvcnRfbG9uZyA8LSB3ZWVrbHlfZWZmb3J0X3dpZGUgJT4lDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gYygiVjIgKDw0MCUpIiwgIlYzICg0MC01MCUpIiwiVjQgKDUwLTYwJSkiICwiVjUgKDYwLTcwJSkiLCAiVjYgKDcwLTgwJSkiLCAiVjcgKDgwLTkwJSkiLCAiVjggKDkwLTEwMCUpIiksDQogICAgbmFtZXNfdG8gPSAicmVsYXRpdmVfYmFuZCIsDQogICAgdmFsdWVzX3RvID0gInBjdF9lZmZvcnQiDQogICkgJT4lDQogIG11dGF0ZSgNCiAgICByZWxhdGl2ZV9iYW5kID0gZmFjdG9yKA0KICAgICAgcmVsYXRpdmVfYmFuZCwNCiAgICAgIGxldmVscyA9IHJldihjKCJWMiAoPDQwJSkiLCAiVjMgKDQwLTUwJSkiLCJWNCAoNTAtNjAlKSIgLCJWNSAoNjAtNzAlKSIsICJWNiAoNzAtODAlKSIsICJWNyAoODAtOTAlKSIsICJWOCAoOTAtMTAwJSkiKSkNCiAgICApDQogICkNCmBgYA0KDQpgYGB7cn0NCiMgUGxvdCBSZWxhdGl2ZSBCYW5kcyBmb3IgSURfMTENCmdncGxvdCh3ZWVrbHlfZWZmb3J0X2xvbmcsIGFlcyh4ID0gd2VlaywgeSA9IHBjdF9lZmZvcnQsIGZpbGwgPSByZWxhdGl2ZV9iYW5kKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcGN0X29mX21heF92ZWxvY2l0eSwgZ3JvdXAgPSAxKSwgDQogICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAxLjIpICsNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHkpLCANCiAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoDQogICAgICAiVjIgKDw0MCUpIiA9ICJkYXJrZ3JlZW4iLA0KICAgICAgIlYzICg0MC01MCUpIiA9ICJncmVlbjIiLA0KICAgICAgIlY0ICg1MC02MCUpIiA9ICJncmVlbnllbGxvdyIsDQogICAgICAiVjUgKDYwLTcwJSkiID0gInllbGxvdyIsDQogICAgICAiVjYgKDcwLTgwJSkiID0gIm9yYW5nZSIsDQogICAgICAiVjcgKDgwLTkwJSkiID0gInRvbWF0byIsDQogICAgICAiVjggKDkwLTEwMCUpIiA9ICJkYXJrcmVkIg0KICAgICkNCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUmVsYXRpdmUgVmVsb2NpdHkgQmFuZCBFZmZvcnQgJSBwZXIgV2VlaywgSURfMTEiLA0KICAgIHggPSAiV2VlayIsDQogICAgeSA9ICJQZXJjZW50IG9mIFdlZWtseSBFZmZvcnRzIiwNCiAgICBmaWxsID0gIlJlbGF0aXZlIFZlbG9jaXR5IEJhbmQiDQogICkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIHNlYy5heGlzID0gc2VjX2F4aXMofiAuLCBuYW1lID0gIlBlcmNlbnQgb2YgTWF4IFZlbG9jaXR5IikNCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQpDb21wYXJpbmcgdGhpcyB3aXRoIHRoZSBhYnNvbHV0ZSBiYW5kcyBncmFwaCBvZiBJRF8xMSwgd2UgY2FuIHNlZSB0aGF0IHRoaXMgaXMgYSBtdWNoIGJldHRlciByZXByZXNlbnRhdGlvbiBvZiB0aGVpciBydW5uaW5nIHNwZWVkcy4NCg0KIyMjIyBSZWxhdGl2ZSBCYW5kcyBmb3IgZWFjaCBhdGhsZXRlDQoNCmBgYHtyfQ0KIyBGdW5jdGlvbiB0byBjcmVhdGUgcmVsYXRpdmUgYmFuZHMgYW5kIHBsb3QgdGhlc2UgZm9yIGVhY2ggYXRobGV0ZQ0KDQojIEFycmFuZ2UgZGF0YSBzbyB0aGF0IHRoZSBwbG90IHdpbGwgZGlzcGxheSBpbiBhbm9uX2lkIG9yZGVyDQpjbGVhbl9hbm9ucyA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBhcnJhbmdlKGFub25faWQpDQoNCiMgTG9vcCBvdmVyIGFsbCBhdGhsZXRlcyBhbmQgY3JlYXRlIHRoZWlyIHJlbGF0aXZlIGJhbmRzIHBsb3RzDQp1bmlxdWUoY2xlYW5fYW5vbnMkYW5vbl9pZCkgJT4lDQogIGxhcHBseShmdW5jdGlvbihwbGF5ZXJfaWQpIHsNCiAgICAjIFByZXBhcmUgZGF0YSBmb3IgcGxheWVyDQogICAgcGxheWVyX2RhdGEgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgICAgIGZpbHRlcihEYXRlID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUNCiAgICAgIGZpbHRlcihhbm9uX2lkID09IHBsYXllcl9pZCwgTWF4aW11bS5WZWxvY2l0eSAhPSAwLCAhaXMubmEoTWF4aW11bS5WZWxvY2l0eSkpICU+JQ0KICAgICAgbXV0YXRlKA0KICAgICAgICB3ZWVrID0gZmxvb3JfZGF0ZShEYXRlLCB1bml0ID0gIndlZWsiKSwNCiAgICAgICAgcGN0X29mX21heCA9IChNYXhpbXVtLlZlbG9jaXR5IC8gUGxheWVyLk1heC5WZWxvY2l0eSkgKiAxMDAsDQogICAgICAgICMgQ3JlYXRlIFJlbGF0aXZlIEJhbmRzDQogICAgICAgIHJlbGF0aXZlX2JhbmQgPSBjdXQoDQogICAgICAgICAgcGN0X29mX21heCwNCiAgICAgICAgICBicmVha3MgPSBjKDAsIDQwLCA1MCwgNjAsIDcwLCA4MCwgOTAsIDEwMCksDQogICAgICAgICAgbGFiZWxzID0gYygiVjIgKDw0MCUpIiwgIlYzICg0MC01MCUpIiwgIlY0ICg1MC02MCUpIiwgIlY1ICg2MC03MCUpIiwNCiAgICAgICAgICAgICAgICAgICAgICJWNiAoNzAtODAlKSIsICJWNyAoODAtOTAlKSIsICJWOCAoOTAtMTAwJSkiKSwNCiAgICAgICAgICByaWdodCA9IFRSVUUsICMgU2V0IHRvIHRydWUgdG8gaW5jbHVkZSAxMDAlDQogICAgICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFICAjIFNldCB0byB0cnVlIHRvIGluY2x1ZGUgMCBpbiAxc3QgaW50ZXJ2YWwNCiAgICAgICAgKSwNCiAgICAgICAgIyBSZXZlcnNlIGZhY3RvciBsZXZlbHMgc28gaGlnaGVzdCBiYW5kIGlzIG9uIHRvcCBpbiBwbG90DQogICAgICAgIHJlbGF0aXZlX2JhbmQgPSBmYWN0b3IoDQogICAgICAgICAgcmVsYXRpdmVfYmFuZCwNCiAgICAgICAgICBsZXZlbHMgPSByZXYoYygiVjIgKDw0MCUpIiwgIlYzICg0MC01MCUpIiwgIlY0ICg1MC02MCUpIiwgIlY1ICg2MC03MCUpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAiVjYgKDcwLTgwJSkiLCAiVjcgKDgwLTkwJSkiLCAiVjggKDkwLTEwMCUpIikpDQogICAgICAgICkNCiAgICAgICkNCiAgICANCiAgICBpZiAobnJvdyhwbGF5ZXJfZGF0YSkgPT0gMCkgcmV0dXJuKE5VTEwpICAjIHNraXAgZW1wdHkNCg0KICAgICMgU3VtbWFyaXplIGNvdW50cyBwZXIgd2VlayBhbmQgcmVsYXRpdmUgYmFuZA0KICAgIHdlZWtseV9lZmZvcnQgPC0gcGxheWVyX2RhdGEgJT4lDQogICAgICBncm91cF9ieSh3ZWVrLCByZWxhdGl2ZV9iYW5kKSAlPiUNCiAgICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogICAgICBncm91cF9ieSh3ZWVrKSAlPiUNCiAgICAgIG11dGF0ZShwY3RfZWZmb3J0ID0gKGNvdW50IC8gc3VtKGNvdW50KSkgKiAxMDApICU+JQ0KICAgICAgdW5ncm91cCgpDQoNCiAgICAjIFBpdm90IHdpZGVyIHRvIGdldCBwZXJjZW50YWdlcyBwZXIgYmFuZCBhcyBjb2x1bW5zDQogICAgd2Vla2x5X2VmZm9ydF93aWRlIDwtIHdlZWtseV9lZmZvcnQgJT4lDQogICAgICBzZWxlY3Qod2VlaywgcmVsYXRpdmVfYmFuZCwgcGN0X2VmZm9ydCkgJT4lDQogICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gcmVsYXRpdmVfYmFuZCwgdmFsdWVzX2Zyb20gPSBwY3RfZWZmb3J0LCB2YWx1ZXNfZmlsbCA9IDApDQoNCiAgICAjIENhbGN1bGF0ZSB3ZWVrbHkgbWF4IHZlbG9jaXR5ICUgb2YgcGxheWVyIG1heCB2ZWxvY2l0eQ0KICAgIHdlZWtseV9tYXhfdmVsb2NpdHkgPC0gcGxheWVyX2RhdGEgJT4lDQogICAgICBncm91cF9ieSh3ZWVrKSAlPiUNCiAgICAgIHN1bW1hcmlzZShwY3Rfb2ZfbWF4X3ZlbG9jaXR5ID0gbWF4KHBjdF9vZl9tYXgpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQogICAgIyBKb2luDQogICAgd2Vla2x5X2VmZm9ydF93aWRlIDwtIHdlZWtseV9lZmZvcnRfd2lkZSAlPiUNCiAgICAgIGxlZnRfam9pbih3ZWVrbHlfbWF4X3ZlbG9jaXR5LCBieSA9ICJ3ZWVrIikNCiAgICANCiAgICAjIFBpdm90IGxvbmdlciBmb3Igc3RhY2tlZCBiYXIgcGxvdHRpbmcNCiAgICB3ZWVrbHlfZWZmb3J0X2xvbmcgPC0gd2Vla2x5X2VmZm9ydF93aWRlICU+JQ0KICAgICAgcGl2b3RfbG9uZ2VyKA0KICAgICAgICAgIGNvbHMgPSAtYyh3ZWVrLCBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwNCiAgICAgICAgbmFtZXNfdG8gPSAicmVsYXRpdmVfYmFuZCIsDQogICAgICAgIHZhbHVlc190byA9ICJwY3RfZWZmb3J0Ig0KICAgICAgKSAlPiUNCiAgICAgIG11dGF0ZSgNCiAgICAgICAgcmVsYXRpdmVfYmFuZCA9IGZhY3RvcigNCiAgICAgICAgICByZWxhdGl2ZV9iYW5kLA0KICAgICAgICAgIGxldmVscyA9IHJldihjKCJWMiAoPDQwJSkiLCAiVjMgKDQwLTUwJSkiLCAiVjQgKDUwLTYwJSkiLCAiVjUgKDYwLTcwJSkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJWNiAoNzAtODAlKSIsICJWNyAoODAtOTAlKSIsICJWOCAoOTAtMTAwJSkiKSkNCiAgICAgICAgKQ0KICAgICAgKQ0KICAgIA0KICAgICMgUGxvdA0KICAgIHBsb3QgPC0gZ2dwbG90KHdlZWtseV9lZmZvcnRfbG9uZywgYWVzKHggPSB3ZWVrLCB5ID0gcGN0X2VmZm9ydCwgZmlsbCA9IHJlbGF0aXZlX2JhbmQpKSArDQogICAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgICAgIGdlb21fbGluZShhZXMoeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHksIGdyb3VwID0gMSksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEuMikgKw0KICAgICAgZ2VvbV9wb2ludChhZXMoeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHkpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSArDQogICAgICBzY2FsZV9maWxsX21hbnVhbCgNCiAgICAgICAgdmFsdWVzID0gYygNCiAgICAgICAgICAiVjIgKDw0MCUpIiA9ICJkYXJrZ3JlZW4iLA0KICAgICAgICAgICJWMyAoNDAtNTAlKSIgPSAiZ3JlZW4yIiwNCiAgICAgICAgICAiVjQgKDUwLTYwJSkiID0gImdyZWVueWVsbG93IiwNCiAgICAgICAgICAiVjUgKDYwLTcwJSkiID0gInllbGxvdyIsDQogICAgICAgICAgIlY2ICg3MC04MCUpIiA9ICJvcmFuZ2UiLA0KICAgICAgICAgICJWNyAoODAtOTAlKSIgPSAidG9tYXRvIiwNCiAgICAgICAgICAiVjggKDkwLTEwMCUpIiA9ICJkYXJrcmVkIg0KICAgICAgICApDQogICAgICApICsNCiAgICAgIHNjYWxlX3lfY29udGludW91cygNCiAgICAgICAgbmFtZSA9ICJQZXJjZW50IG9mIFdlZWtseSBFZmZvcnQiLA0KICAgICAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4gLiwgbmFtZSA9ICJQZXJjZW50IG9mIE1heCBWZWxvY2l0eSIpDQogICAgICApICsNCiAgICAgIGxhYnMoDQogICAgICAgIHRpdGxlID0gcGFzdGUoIlJlbGF0aXZlIFZlbG9jaXR5IEJhbmQgRWZmb3J0ICUgYW5kIE1heCBWZWxvY2l0eSBUcmVuZCBmb3IgUGxheWVyIiwgcGxheWVyX2lkKSwNCiAgICAgICAgeCA9ICJXZWVrIiwNCiAgICAgICAgZmlsbCA9ICJSZWxhdGl2ZSBWZWxvY2l0eSBCYW5kIg0KICAgICAgKSArDQogICAgICB0aGVtZV9jbGFzc2ljKCkNCg0KICAgIHByaW50KHBsb3QpDQogICAgDQogICAgcmV0dXJuKE5VTEwpDQogIH0pDQpgYGANCg0KDQpgYGB7cn0NCiMgQ3JlYXRlIHJlbGF0aXZlIGJhbmRzIGZvciBhbGwgYXRobGV0ZXMNCg0KIyBEZWZpbmUgdGhlIHZlbG9jaXR5IGJhbmQgbGFiZWxzDQpiYW5kX2xldmVscyA8LSBjKCJWMiAoPDQwJSkiLCAiVjMgKDQwLTUwJSkiLCAiVjQgKDUwLTYwJSkiLCAiVjUgKDYwLTcwJSkiLA0KICAgICAgICAgICAgICAgICAgICAgIlY2ICg3MC04MCUpIiwgIlY3ICg4MC05MCUpIiwgIlY4ICg5MC0xMDAlKSIpDQoNCiMgQ3JlYXRlIGV4cG9zdXJlIGRhdGEgZnJhbWUgZm9yIGFsbCBwbGF5ZXJzDQp3ZWVrbHlfc3ByaW50X2V4cG9zdXJlIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihEYXRlID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUNCiAgZmlsdGVyKE1heGltdW0uVmVsb2NpdHkgIT0gMCwgIWlzLm5hKE1heGltdW0uVmVsb2NpdHkpKSAlPiUNCiAgbXV0YXRlKA0KICAgIHdlZWsgPSBmbG9vcl9kYXRlKERhdGUsIHVuaXQgPSAid2VlayIpLA0KICAgIHBjdF9vZl9tYXggPSAoTWF4aW11bS5WZWxvY2l0eSAvIFBsYXllci5NYXguVmVsb2NpdHkpICogMTAwLA0KICAgIHJlbGF0aXZlX2JhbmQgPSBjdXQoDQogICAgICBwY3Rfb2ZfbWF4LA0KICAgICAgYnJlYWtzID0gYygwLCA0MCwgNTAsIDYwLCA3MCwgODAsIDkwLCAxMDAuMSksDQogICAgICBsYWJlbHMgPSBiYW5kX2xldmVscywNCiAgICAgIHJpZ2h0ID0gVFJVRSwgICMgd2lsbCBpbmNsdWRlIDEwMA0KICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFDQogICAgKSwNCiAgICByZWxhdGl2ZV9iYW5kID0gZmFjdG9yKHJlbGF0aXZlX2JhbmQsIGxldmVscyA9IGJhbmRfbGV2ZWxzKQ0KICApICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCB3ZWVrLCByZWxhdGl2ZV9iYW5kKSAlPiUNCiAgc3VtbWFyaXNlKGVmZm9ydF9jb3VudCA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIHdlZWspICU+JQ0KICBtdXRhdGUoDQogICAgdG90YWxfZWZmb3J0cyA9IHN1bShlZmZvcnRfY291bnQpLA0KICAgIHBjdF9lZmZvcnQgPSAoZWZmb3J0X2NvdW50IC8gdG90YWxfZWZmb3J0cykgKiAxMDANCiAgKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBzZWxlY3QoYW5vbl9pZCwgd2VlaywgcmVsYXRpdmVfYmFuZCwgcGN0X2VmZm9ydCkgJT4lDQogIHBpdm90X3dpZGVyKA0KICAgIG5hbWVzX2Zyb20gPSByZWxhdGl2ZV9iYW5kLA0KICAgIHZhbHVlc19mcm9tID0gcGN0X2VmZm9ydCwNCiAgICB2YWx1ZXNfZmlsbCA9IDANCiAgKQ0KDQojIEFkZCB3ZWVrbHkgbWF4IHZlbG9jaXR5ICUgb2YgbWF4DQp3ZWVrbHlfbWF4X3BjdCA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBmaWx0ZXIoRGF0ZSA+PSBhcy5EYXRlKCIyMDI0LTA2LTMwIikgJiBEYXRlIDw9IGFzLkRhdGUoIjIwMjUtMDctMDEiKSkgJT4lDQogIGZpbHRlcihNYXhpbXVtLlZlbG9jaXR5ICE9IDAsICFpcy5uYShNYXhpbXVtLlZlbG9jaXR5KSkgJT4lDQogIG11dGF0ZSgNCiAgICB3ZWVrID0gZmxvb3JfZGF0ZShEYXRlLCB1bml0ID0gIndlZWsiKSwNCiAgICBwY3Rfb2ZfbWF4ID0gKE1heGltdW0uVmVsb2NpdHkgLyBQbGF5ZXIuTWF4LlZlbG9jaXR5KSAqIDEwMA0KICApICU+JQ0KICBncm91cF9ieShhbm9uX2lkLCB3ZWVrKSAlPiUNCiAgc3VtbWFyaXNlKHBjdF9vZl9tYXhfdmVsb2NpdHkgPSBtYXgocGN0X29mX21heCwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikNCg0KIyBGaW5hbCBkYXRhIHNldA0KcmVsYXRpdmVfYmFuZHMgPC0gd2Vla2x5X3NwcmludF9leHBvc3VyZSAlPiUNCiAgbGVmdF9qb2luKHdlZWtseV9tYXhfcGN0LCBieSA9IGMoImFub25faWQiLCAid2VlayIpKQ0KDQojIENvbnZlcnQgdG8gbG9uZyBmb3IgcGxvdHRpbmcNCnJlbGF0aXZlX2JhbmRzX2xvbmcgPC0gcmVsYXRpdmVfYmFuZHMgJT4lDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gc3RhcnRzX3dpdGgoIlYiKSwNCiAgICBuYW1lc190byA9ICJyZWxhdGl2ZV9iYW5kIiwNCiAgICB2YWx1ZXNfdG8gPSAicGN0X2VmZm9ydCINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIHJlbGF0aXZlX2JhbmQgPSBmYWN0b3IocmVsYXRpdmVfYmFuZCwgbGV2ZWxzID0gYmFuZF9sZXZlbHMpDQogICkNCmBgYA0KDQpgYGB7cn0NCkNPTUJPIDwtIGMoIlFCIiwiTEIiLCJURSIsIlJCIiwgIklMQiIpDQpCSUcgPC0gYygiT0wiLCAiREwiLCAiREUiLCAiRFQiKQ0KU0tJTEwgPC0gYygiV1IiLCAiREIiLCAiQ0IiLCAiU0FGIikNClBvc2l0aW9ucyA8LSBjKCJDT01CTyIsICJCSUciLCAiU0tJTEwiKQ0KDQp3ZWVrbHlfc3ByaW50X2V4cG9zdXJlIDwtIGxlZnRfam9pbih3ZWVrbHlfc3ByaW50X2V4cG9zdXJlLHBsYXllcl9wb3NpdGlvbnMsIGJ5ID0gImFub25faWQiLCByZWxhdGlvbnNoaXAgPSAibWFueS10by1tYW55IikNCg0Kd2Vla2x5X3NwcmludF9leHBvc3VyZSA8LSB3ZWVrbHlfc3ByaW50X2V4cG9zdXJlICU+JQ0KICBtdXRhdGUoUG9zaXRpb24gPSBjYXNlX3doZW4oUHJpbWFyeS5Qb3NpdGlvbiAlaW4lIENPTUJPIH4gIkNPTUJPIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQcmltYXJ5LlBvc2l0aW9uICVpbiUgQklHIH4gIkJJRyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUHJpbWFyeS5Qb3NpdGlvbiAlaW4lIFNLSUxMIH4gIlNLSUxMIikpDQoNCndlZWtseV9zcHJpbnRfZXhwb3N1cmUgPC0gZGlzdGluY3Qod2Vla2x5X3NwcmludF9leHBvc3VyZSkNCg0KbWF4X3ZlbG9jaXRpZXMgPC0gd2Vla2x5X3ZlbG9jaXR5X2VmZm9ydHNbLGMoImFub25faWQiLCJ3ZWVrIiwicGN0X29mX21heF92ZWxvY2l0eSIpXQ0KDQp3ZWVrbHlfc3ByaW50X2V4cG9zdXJlIDwtIGxlZnRfam9pbih3ZWVrbHlfc3ByaW50X2V4cG9zdXJlLCBtYXhfdmVsb2NpdGllcywgYnkgPSBjKCJhbm9uX2lkIiwgIndlZWsiKSwgcmVsYXRpb25zaGlwPSJtYW55LXRvLW1hbnkiKQ0KDQoNCg0KDQpzcHJpbnRfZXhwb3N1cmVfYmlnIDwtIHdlZWtseV9zcHJpbnRfZXhwb3N1cmUgJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiQklHIikgJT4lDQogIGdyb3VwX2J5KHdlZWspICU+JQ0KICBtdXRhdGUoYXZnX3BjdF9tYXggPSBtZWFuKHBjdF9vZl9tYXhfdmVsb2NpdHkpLA0KICAgICAgICAgYXZnX1Y4ID0gbWVhbihgVjggKDkwLTEwMCUpYCksDQogICAgICAgICBhdmdfVjcgPSBtZWFuKGBWNyAoODAtOTAlKWApLA0KICAgICAgICAgYXZnX1Y2ID0gbWVhbihgVjYgKDcwLTgwJSlgKSwNCiAgICAgICAgIGF2Z19WNSA9IG1lYW4oYFY1ICg2MC03MCUpYCksDQogICAgICAgICBhdmdfVjQgPSBtZWFuKGBWNCAoNTAtNjAlKWApLA0KICAgICAgICAgYXZnX1YzID0gbWVhbihgVjMgKDQwLTUwJSlgKSwNCiAgICAgICAgIGF2Z19WMiA9IG1lYW4oYFYyICg8NDAlKWApKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBuYS5vbWl0KCkNCg0Kc3ByaW50X2V4cG9zdXJlX2NvbWJvIDwtIHdlZWtseV9zcHJpbnRfZXhwb3N1cmUgJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiQ09NQk8iKSAgJT4lDQogIGdyb3VwX2J5KHdlZWspICU+JQ0KICBtdXRhdGUoYXZnX3BjdF9tYXggPSBtZWFuKHBjdF9vZl9tYXhfdmVsb2NpdHkpLA0KICAgICAgICAgYXZnX1Y4ID0gbWVhbihgVjggKDkwLTEwMCUpYCksDQogICAgICAgICBhdmdfVjcgPSBtZWFuKGBWNyAoODAtOTAlKWApLA0KICAgICAgICAgYXZnX1Y2ID0gbWVhbihgVjYgKDcwLTgwJSlgKSwNCiAgICAgICAgIGF2Z19WNSA9IG1lYW4oYFY1ICg2MC03MCUpYCksDQogICAgICAgICBhdmdfVjQgPSBtZWFuKGBWNCAoNTAtNjAlKWApLA0KICAgICAgICAgYXZnX1YzID0gbWVhbihgVjMgKDQwLTUwJSlgKSwNCiAgICAgICAgIGF2Z19WMiA9IG1lYW4oYFYyICg8NDAlKWApKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBuYS5vbWl0KCkNCg0Kc3ByaW50X2V4cG9zdXJlX3NraWxsIDwtIHdlZWtseV9zcHJpbnRfZXhwb3N1cmUgJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiU0tJTEwiKSAgJT4lDQogIGdyb3VwX2J5KHdlZWspICU+JQ0KICBtdXRhdGUoYXZnX3BjdF9tYXggPSBtZWFuKHBjdF9vZl9tYXhfdmVsb2NpdHkpLA0KICAgICAgICAgYXZnX1Y4ID0gbWVhbihgVjggKDkwLTEwMCUpYCksDQogICAgICAgICBhdmdfVjcgPSBtZWFuKGBWNyAoODAtOTAlKWApLA0KICAgICAgICAgYXZnX1Y2ID0gbWVhbihgVjYgKDcwLTgwJSlgKSwNCiAgICAgICAgIGF2Z19WNSA9IG1lYW4oYFY1ICg2MC03MCUpYCksDQogICAgICAgICBhdmdfVjQgPSBtZWFuKGBWNCAoNTAtNjAlKWApLA0KICAgICAgICAgYXZnX1YzID0gbWVhbihgVjMgKDQwLTUwJSlgKSwNCiAgICAgICAgIGF2Z19WMiA9IG1lYW4oYFYyICg8NDAlKWApKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBuYS5vbWl0KCkNCiAgDQpgYGANCg0KV2hlbiBJIGJ1aWx0IHRoZSBtb2RlbHMgdGhhdCBjb25zaWRlcmVkIHRoZSByZWxhdGl2ZSBiYW5kcyBpbnN0ZWFkIG9mIHRoZSBhYnNvbHV0ZSBiYW5kcyBwcm92aWRlZCwgSSBmb3VuZCB0aGF0IGluaXRpYWxseSwgdGhlIHJlc3VsdHMgd2VyZSBhIGxvdCBiZXR0ZXIgdGhhbiB0aGUgcHJldmlvdXMgbW9kZWxzIHdpdGggdGhlIGFic29sdXRlIGJhbmRzLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlIHJlbGF0aXZlIGJvdW5kcyB3aGljaCBhcmUgc2VnbWVudGVkIGludG8gMTAlIGNodW5rcyBvZiBhbGwgdGltZSBtYXhpbXVtIHZlbG9jaXR5IGZvciBlYWNoIHBsYXllciBpcyBhIGxvdCBtb3JlIGluZGljYXRpdmUgb2YgZWZmb3J0IGFuZCB0aGVyZWZvcmUgdGhlaXIgcGVyY2VudGFnZSBvZiBtYXhpbXVtIHZlbG9jaXR5IGluIGEgZ2l2ZW4gd2Vlay4gV2UgYXJlIGFibGUgdG8gc2VlIHRob3VnaCB0aGF0IHRoZXJlIGlzIGEgbG90IG9mIG11bHRpY29sbGluZWFyaXR5IHdpdGhpbiB0aGUgcmVsYXRpdmUgYmFuZHMgY2FsY3VsYXRlZC4gDQoNCmBgYHtyfQ0KIyBNb2RlbGluZw0KDQojIEJpZ3MgTW9kZWwNCm1vZGVsX2JpZyA8LSBsbShhdmdfcGN0X21heH5hdmdfVjIrYXZnX1YzK2F2Z19WNCthdmdfVjUrYXZnX1Y2K2F2Z19WNythdmdfVjgsDQogICAgICAgICAgICAgICAgZGF0YT1zcHJpbnRfZXhwb3N1cmVfYmlnKQ0Kc3VtbWFyeShtb2RlbF9iaWcpDQoNCg0KIyBTa2lsbCBNb2RlbA0KbW9kZWxfc2tpbGwgPC0gbG0oYXZnX3BjdF9tYXh+YXZnX1YyK2F2Z19WMythdmdfVjQrYXZnX1Y1K2F2Z19WNithdmdfVjcrYXZnX1Y4LA0KICAgICAgICAgICAgICAgIGRhdGE9c3ByaW50X2V4cG9zdXJlX3NraWxsKQ0Kc3VtbWFyeShtb2RlbF9za2lsbCkNCg0KDQojIENvbWJvIE1vZGVsDQptb2RlbF9jb21ibyA8LSBsbShhdmdfcGN0X21heH5hdmdfVjIrYXZnX1YzK2F2Z19WNCthdmdfVjUrYXZnX1Y2K2F2Z19WNythdmdfVjgsDQogICAgICAgICAgICAgICBkYXRhPXNwcmludF9leHBvc3VyZV9jb21ibykNCnN1bW1hcnkobW9kZWxfY29tYm8pDQoNCmBgYA0KDQpMb29raW5nIGF0IHRoZSBjb3JyZWxhdGlvbnMgYmV0d2VlbiBhbGwgb2YgdGhlIHByZWRpY3RvcnMgYXMgd2VsbCBhcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUgaW4gdGhlIG1vZGVsIHdlIGNhbiBzZWUgdGhhdCBhIGxvdCBvZiB0aGUgcHJlZGljdG9ycyBoYXZlIHN1cGVyIHN0cm9uZyBjb3JyZWxhdGlvbnMgd2hpY2ggZWFjaCBvdGhlciwgc29tZSBtb3JlIHNvIHRoYW4gdGhleSBhcmUgd2l0aCB0aGUgcmVzcG9uc2UuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgaXNzdWUgd2l0aCB0aGUgbW9kZWxzIGFib3ZlIGlzIHRoYXQgbXVsdGljb2xsaW5lYXJpdHkgaXMgYm9nZ2luZyB0aGVtIGRvd24gYW5kIHdlIGFyZSBub3Qgc2VlaW5nIHRoZSB0cnVlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGUgcHJlZGljdG9ycyBhbmQgdGhlIHJlc3BvbnNlLiANCg0KYGBge3IgTG9va2luZyBhdCBjb2xsaW5lYXIgcHJlZGljdG9yc30NCmNvcihzcHJpbnRfZXhwb3N1cmVfYmlnWywxMzoyMF0pDQpjb3Ioc3ByaW50X2V4cG9zdXJlX3NraWxsWywxMzoyMF0pDQpjb3Ioc3ByaW50X2V4cG9zdXJlX2NvbWJvWywxMzoyMF0pDQpgYGANCg0KQWxsIG9mIHRoZSBtb2RlbHMgYmVsb3cgYXJlIHRoZSBiZXN0IDMgcHJlZGljdG9yIG1vZGVscyB0aGF0IHJlc3VsdGVkIGZvciBlYWNoIHBvc2l0aW9uIGFmdGVyIHJ1bm5pbmcgdGhlIGJlc3Qgc3Vic2V0cyBhbGdvcml0aG0uIEFsbCBvZiB0aGUgZm9sbG93aW5nIGhhdmUgcmVkdWN0aW9ucyBpbiBhZGp1c3RlZC0kUl4yJCBidXQgdGhleSBkb24ndCBzZWVtIHNpZ25pZmljYW50IGNvbnNpZGVyaW5nIHRoYXQgd2UgdG9vayBvdXQgb3ZlciBoYWxmIG9mIHRoZSBwcmVkaWN0b3JzIGFuZCBtYWludGFpbmVkIGEgcmVsYXRpdmVseSBzaW1pbGFyIGFkanVzdGVkLSRSXjIkIHZhbHVlLiANCg0KYGBge3IgcnVubmluZyBiZXN0IHN1YnNldHMgb24gdGhlIG1vZGVscyB0byBsaW1pdCBjb2xsaW5lYXJpdHl9DQojQmlncw0KDQojYmVzdF9zdWJfYmlnIDwtIHJlZ3N1YnNldHMoYXZnX3BjdF9tYXh+YXZnX1YyK2F2Z19WMythdmdfVjQrYXZnX1Y1K2F2Z19WNithdmdfVjcrYXZnX1Y4LA0KIyAgICAgICAgICAgICAgICBkYXRhPXNwcmludF9leHBvc3VyZV9iaWcsIG1ldGhvZD0iZXhoYXVzdGl2ZSIpDQojc3VtbWFyeShiZXN0X3N1Yl9iaWcpDQoNCm1vZGVsX2JpZyA8LSBsbShhdmdfcGN0X21heH5hdmdfVjIrYXZnX1Y2K2F2Z19WNywNCiAgICAgICAgICAgICAgICBkYXRhPXNwcmludF9leHBvc3VyZV9iaWcpDQpzdW1tYXJ5KG1vZGVsX2JpZykNCg0KDQojU2tpbGxzDQoNCiNiZXN0X3N1Yl9za2lsbCA8LSByZWdzdWJzZXRzKGF2Z19wY3RfbWF4fmF2Z19WMithdmdfVjMrYXZnX1Y0K2F2Z19WNSthdmdfVjYrYXZnX1Y3K2F2Z19WOCwNCiMgICAgICAgICAgICAgICAgZGF0YT1zcHJpbnRfZXhwb3N1cmVfc2tpbGwsIG1ldGhvZD0iZXhoYXVzdGl2ZSIpDQojc3VtbWFyeShiZXN0X3N1Yl9za2lsbCkNCg0KbW9kZWxfc2tpbGwgPC0gbG0oYXZnX3BjdF9tYXh+YXZnX1YyK2F2Z19WNSthdmdfVjYsDQogICAgICAgICAgICAgICAgZGF0YT1zcHJpbnRfZXhwb3N1cmVfc2tpbGwpDQpzdW1tYXJ5KG1vZGVsX3NraWxsKQ0KDQojQ29tYm9zDQojYmVzdF9zdWJfY29tYm8gPC0gcmVnc3Vic2V0cyhhdmdfcGN0X21heH5hdmdfVjIrYXZnX1YzK2F2Z19WNCthdmdfVjUrYXZnX1Y2K2F2Z19WNythdmdfVjgsDQojICAgICAgICAgICAgICAgIGRhdGE9c3ByaW50X2V4cG9zdXJlX2NvbWJvLCBtZXRob2Q9ImV4aGF1c3RpdmUiKQ0KI3N1bW1hcnkoYmVzdF9zdWJfY29tYm8pDQoNCm1vZGVsX2NvbWJvIDwtIGxtKGF2Z19wY3RfbWF4fmF2Z19WMithdmdfVjQrYXZnX1Y1LA0KICAgICAgICAgICAgICAgZGF0YT1zcHJpbnRfZXhwb3N1cmVfY29tYm8pDQpzdW1tYXJ5KG1vZGVsX2NvbWJvKQ0KYGBgDQpTaW5jZSB0aGUgbW9kZWxzIHBlcmZvcm0gcm91Z2hseSB0aGUgc2FtZSB3aXRoIGp1c3QgMyBwcmVkaWN0b3JzIGFzIHRoZXkgZG8gd2l0aCBhbGwgNywgaXQgbWF5IG1ha2Ugc2Vuc2UgdG8gaW5zdGVhZCB0cnVuY2F0ZSBpbnRvIG1vcmUgZ2VuZXJhbCBiaW5zLiBUaGlzIG1heSBoZWxwIHVzIHVuZGVyc3RhbmQgdGhlIHJlbGF0aW9uc2hpcCB3aXRoIGxvdywgbWVkaXVtIGFuZCBoaWdoIGVmZm9ydCB3aXRoIHdlZWtseSBtYXhpbXVtIHZlbG9jaXR5LiANCg0KYGBge3J9DQpzcHJpbnRfZXhwb3N1cmVfYmlnIDwtIHNwcmludF9leHBvc3VyZV9iaWcgJT4lDQogIG11dGF0ZShhdmdfbG93ID0gKGF2Z19WMithdmdfVjMpLzIsDQogICAgICAgICBhdmdfbWVkaXVtID0gKGF2Z19WNCthdmdfVjUrYXZnX1Y2KS8zLA0KICAgICAgICAgYXZnX2hpZ2ggPSAoYXZnX1Y3K2F2Z19WOCkvMikNCg0Kc3ByaW50X2V4cG9zdXJlX3NraWxsIDwtIHNwcmludF9leHBvc3VyZV9za2lsbCAlPiUNCiAgbXV0YXRlKGF2Z19sb3cgPSAoYXZnX1YyK2F2Z19WMykvMiwNCiAgICAgICAgIGF2Z19tZWRpdW0gPSAoYXZnX1Y0K2F2Z19WNSthdmdfVjYpLzMsDQogICAgICAgICBhdmdfaGlnaCA9IChhdmdfVjcrYXZnX1Y4KS8yKQ0KDQpzcHJpbnRfZXhwb3N1cmVfY29tYm8gPC0gc3ByaW50X2V4cG9zdXJlX2NvbWJvICU+JQ0KICBtdXRhdGUoYXZnX2xvdyA9IChhdmdfVjIrYXZnX1YzKS8yLA0KICAgICAgICAgYXZnX21lZGl1bSA9IChhdmdfVjQrYXZnX1Y1K2F2Z19WNikvMywNCiAgICAgICAgIGF2Z19oaWdoID0gKGF2Z19WNythdmdfVjgpLzIpDQpgYGANCg0KDQpgYGB7cn0NCiNiaWdzDQptb2RlbF9iaWdfZ2VuZXJhbCA8LSBsbShhdmdfcGN0X21heH5hdmdfbG93K2F2Z19tZWRpdW0rYXZnX2hpZ2gsDQogICAgICAgICAgICAgICAgICAgICAgICBkYXRhPXNwcmludF9leHBvc3VyZV9iaWcpDQpzdW1tYXJ5KG1vZGVsX2JpZ19nZW5lcmFsKQ0KDQojc2tpbGxzDQptb2RlbF9za2lsbF9nZW5lcmFsIDwtIGxtKGF2Z19wY3RfbWF4fmF2Z19sb3crYXZnX21lZGl1bSthdmdfaGlnaCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9c3ByaW50X2V4cG9zdXJlX3NraWxsKQ0Kc3VtbWFyeShtb2RlbF9za2lsbF9nZW5lcmFsKQ0KDQojY29tYm9zDQptb2RlbF9jb21ib19nZW5lcmFsIDwtIGxtKGF2Z19wY3RfbWF4fmF2Z19sb3crYXZnX21lZGl1bSthdmdfaGlnaCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE9c3ByaW50X2V4cG9zdXJlX2NvbWJvKQ0Kc3VtbWFyeShtb2RlbF9jb21ib19nZW5lcmFsKQ0KYGBgDQpTdGlsbCBwcmV0dHkgYmFkLCBJIGFtIGdvaW5nIHRvIHVzZSBwcmluY2lwbGUgY29tcG9uZW50cyByZWdyZXNzaW9uIHRvIHNlZSBpZiB0aGVyZSBhcmUgb3RoZXIgcmVsYXRpb25zaGlwcyB3ZSBhcmUgbWlzc2luZw0KDQpgYGB7cn0NCnByZWRzIDwtIHNwcmludF9leHBvc3VyZV9iaWdbLDE0OjIwXQ0KDQpwY19sb2FkaW5ncyA8LSBwcmNvbXAocHJlZHMsIHNjYWxlPVRSVUUpJHJvdGF0aW9uWyxjKDEsMiwzKV0NCnBjX2xvYWRpbmdzDQpgYGANCg0KDQojIyBIb3cgZG9lcyBzcHJpbnRpbmcgZXhwb3N1cmUgKCMgb2YgZWZmb3J0cywgJSBtYXggcmVhY2hlZCkgcmVsYXRlIHRvIGluY2lkZW5jZSBvZiBoYW1zdHJpbmcgaW5qdXJpZXM/DQoNCmBgYHtyfQ0KIyBKb2luIHJlbGF0aXZlX2JhbmRzIGRhdGEgc2V0IHdpdGggaW5qdXJ5IGRhdGENCg0KSW5jaWRlbnRfZGF0ZXMgPC0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuICU+JQ0KICBmaWx0ZXIoRGF0ZSA+PSBhcy5EYXRlKCIyMDI0LTA2LTMwIikgJiBEYXRlIDw9IGFzLkRhdGUoIjIwMjUtMDctMDEiKSkNCg0KIyBHZXQgaWRzIG9mIGF0aGxldGVzIHdpdGggaW5qdXJ5DQppbmp1cmVkX2lkcyA8LSB1bmlxdWUoSW5jaWRlbnRfZGF0ZXMkYW5vbl9pZCkNCg0KIyBGaWx0ZXIgcmVsYXRpdmVfYmFuZHMgdG8gb25seSBpbmNsdWRlIGF0aGxldGVzIHdobyBnb3QgaW5qdXJlZA0KaW5qdXJlZF9kYXRhIDwtIHJlbGF0aXZlX2JhbmRzX2xvbmcgJT4lDQogIGZpbHRlcihhbm9uX2lkICVpbiUgaW5qdXJlZF9pZHMpDQoNCiMgQ3JlYXRlIGluanVyeSB3ZWVrcw0KaW5qdXJ5X3dlZWtzIDwtIEluY2lkZW50X1JlcG9ydF9jbGVhbiAlPiUNCiAgbXV0YXRlKA0KICAgIHdlZWsgPSBmbG9vcl9kYXRlKGFzLkRhdGUoRGF0ZS5vZi5Jbmp1cnkpLCB1bml0ID0gIndlZWsiKQ0KICApICU+JQ0KICBmaWx0ZXIoRGF0ZS5vZi5Jbmp1cnkgPj0gYXMuRGF0ZSgiMjAyNC0wNi0zMCIpICYgRGF0ZS5vZi5Jbmp1cnkgPD0gYXMuRGF0ZSgiMjAyNS0wNy0wMSIpKSAlPiUNCiAgc2VsZWN0KGFub25faWQsIHdlZWspICU+JQ0KICBkaXN0aW5jdCgpICU+JQ0KICBtdXRhdGUoaW5qdXJ5ID0gMSkNCg0KaW5qdXJpZXNfYW5kX3J1bm5pbmcgPC0gaW5qdXJlZF9kYXRhICU+JQ0KICBsZWZ0X2pvaW4oaW5qdXJ5X3dlZWtzLCBieSA9IGMoImFub25faWQiLCAid2VlayIpKSAlPiUNCiAgbXV0YXRlKGluanVyeSA9IGlmZWxzZShpcy5uYShpbmp1cnkpLCAwLCBpbmp1cnkpKQ0KYGBgDQoNCg0KYGBge3J9DQojIEZvciBkZW1vbnN0cmF0aW9uOiBwaWNrIG9uZSBwbGF5ZXIgKG9yIGxvb3Agb3ZlciB0aGVtKQ0KcGxheWVyX2lkIDwtICJJRF8zMDciDQoNCmluanVyaWVzX2FuZF9ydW5uaW5nIDwtIGluanVyaWVzX2FuZF9ydW5uaW5nICU+JQ0KICBtdXRhdGUocmVsYXRpdmVfYmFuZCA9IGZhY3RvcihyZWxhdGl2ZV9iYW5kLCBsZXZlbHMgPSBiYW5kX2xldmVscykpDQoNCnBsb3RfZGF0YSA8LSBpbmp1cmllc19hbmRfcnVubmluZyAlPiUNCiAgZmlsdGVyKGFub25faWQgPT0gcGxheWVyX2lkKQ0KDQojIEZpbmQgaW5qdXJ5IHdlZWtzIGZvciB0aGlzIHBsYXllcg0KaW5qdXJ5X3dlZWtfbnVtcyA8LSBwbG90X2RhdGEgJT4lIGZpbHRlcihpbmp1cnkgPT0gMSkgJT4lIHB1bGwod2VlaykNCg0KZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSB3ZWVrKSkgKw0KICAjIEhpZ2hsaWdodCBpbmp1cnkgd2VlayhzKSBhcyBzaGFkZWQgcmVjdGFuZ2xlcw0KICBnZW9tX3JlY3QoZGF0YSA9IGRhdGEuZnJhbWUod2VlayA9IGluanVyeV93ZWVrX251bXMpLA0KICAgICAgICAgICAgYWVzKHhtaW4gPSB3ZWVrIC0gMywgeG1heCA9IHdlZWsgKyAzLCB5bWluID0gLUluZiwgeW1heCA9IEluZiksDQogICAgICAgICAgICBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMiwgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKw0KICBnZW9tX2NvbChhZXMoeSA9IHBjdF9lZmZvcnQsIGZpbGwgPSByZWxhdGl2ZV9iYW5kKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayhyZXZlcnNlID0gVFJVRSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcGN0X29mX21heF92ZWxvY2l0eSwgZ3JvdXAgPSAxKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMS4yKSArDQogIGdlb21fcG9pbnQoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgKw0KICBzY2FsZV9maWxsX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKA0KICAgICAgIlYyICg8NDAlKSIgPSAiZGFya2dyZWVuIiwNCiAgICAgICJWMyAoNDAtNTAlKSIgPSAiZ3JlZW4yIiwNCiAgICAgICJWNCAoNTAtNjAlKSIgPSAiZ3JlZW55ZWxsb3ciLA0KICAgICAgIlY1ICg2MC03MCUpIiA9ICJ5ZWxsb3ciLA0KICAgICAgIlY2ICg3MC04MCUpIiA9ICJvcmFuZ2UiLA0KICAgICAgIlY3ICg4MC05MCUpIiA9ICJ0b21hdG8iLA0KICAgICAgIlY4ICg5MC0xMDAlKSIgPSAiZGFya3JlZCINCiAgICApDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gcGFzdGUoIlJlbGF0aXZlIFZlbG9jaXR5IEJhbmQgRWZmb3J0ICUgYW5kIE1heCBWZWxvY2l0eSBUcmVuZCBmb3IiLCBwbGF5ZXJfaWQpLA0KICAgIHggPSAiV2VlayIsDQogICAgeSA9ICJQZXJjZW50IG9mIFdlZWtseSBFZmZvcnRzIiwNCiAgICBmaWxsID0gIlJlbGF0aXZlIFZlbG9jaXR5IEJhbmQiDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4sIG5hbWUgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkiKQ0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KDQpgYGB7cn0NCiMgTG9vcCBvdmVyIGluanVyZWQgYXRobGV0ZXMgYW5kIGNyZWF0ZSB0aGVpciBwbG90cw0KDQp1bmlxdWUoaW5qdXJpZXNfYW5kX3J1bm5pbmckYW5vbl9pZCkgJT4lDQogIGxhcHBseShmdW5jdGlvbihwbGF5ZXJfaWQpIHsNCiAgICANCiAgICAjIEZpbHRlciBkYXRhIGZvciB0aGlzIHBsYXllcg0KICAgIHBsb3RfZGF0YSA8LSBpbmp1cmllc19hbmRfcnVubmluZyAlPiUNCiAgICAgIGZpbHRlcihhbm9uX2lkID09IHBsYXllcl9pZCkNCiAgICANCiAgICBpZiAobnJvdyhwbG90X2RhdGEpID09IDApIHJldHVybihOVUxMKSAgIyBTa2lwIGlmIG5vIGRhdGENCiAgICANCiAgICAjIEdldCBpbmp1cnkgd2Vla3MNCiAgICBpbmp1cnlfd2Vla19udW1zIDwtIHBsb3RfZGF0YSAlPiUNCiAgICAgIGZpbHRlcihpbmp1cnkgPT0gMSkgJT4lDQogICAgICBwdWxsKHdlZWspDQogICAgDQogICAgIyBQbG90DQogICAgcGxvdCA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IHdlZWspKSArDQogICAgICAjIEhpZ2hsaWdodCBpbmp1cnkgd2Vla3Mgd2l0aCBzaGFkZWQgYmx1ZSBhcmVhcw0KICAgICAgZ2VvbV9yZWN0KGRhdGEgPSBkYXRhLmZyYW1lKHdlZWsgPSBpbmp1cnlfd2Vla19udW1zKSwNCiAgICAgICAgICAgICAgICBhZXMoeG1pbiA9IHdlZWsgLSAzLCB4bWF4ID0gd2VlayArIDMsIHltaW4gPSAtSW5mLCB5bWF4ID0gSW5mKSwNCiAgICAgICAgICAgICAgICBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMiwgaW5oZXJpdC5hZXMgPSBGQUxTRSkgKw0KICAgICAgZ2VvbV9jb2woYWVzKHkgPSBwY3RfZWZmb3J0LCBmaWxsID0gcmVsYXRpdmVfYmFuZCksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2socmV2ZXJzZSA9IFRSVUUpKSArDQogICAgICBnZW9tX2xpbmUoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCBncm91cCA9IDEpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAxLjIpICsNCiAgICAgIGdlb21fcG9pbnQoYWVzKHkgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMikgKw0KICAgICAgc2NhbGVfZmlsbF9tYW51YWwoDQogICAgICAgIHZhbHVlcyA9IGMoDQogICAgICAgICAgIlYyICg8NDAlKSIgPSAiZGFya2dyZWVuIiwNCiAgICAgICAgICAiVjMgKDQwLTUwJSkiID0gImdyZWVuMiIsDQogICAgICAgICAgIlY0ICg1MC02MCUpIiA9ICJncmVlbnllbGxvdyIsDQogICAgICAgICAgIlY1ICg2MC03MCUpIiA9ICJ5ZWxsb3ciLA0KICAgICAgICAgICJWNiAoNzAtODAlKSIgPSAib3JhbmdlIiwNCiAgICAgICAgICAiVjcgKDgwLTkwJSkiID0gInRvbWF0byIsDQogICAgICAgICAgIlY4ICg5MC0xMDAlKSIgPSAiZGFya3JlZCINCiAgICAgICAgKQ0KICAgICAgKSArDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgICAgIG5hbWUgPSAiUGVyY2VudCBvZiBXZWVrbHkgRWZmb3J0IiwNCiAgICAgICAgc2VjLmF4aXMgPSBzZWNfYXhpcyh+IC4sIG5hbWUgPSAiUGVyY2VudCBvZiBNYXggVmVsb2NpdHkiKQ0KICAgICAgKSArDQogICAgICBsYWJzKA0KICAgICAgICB0aXRsZSA9IHBhc3RlKCJWZWxvY2l0eSBCYW5kIEV4cG9zdXJlICYgTWF4ICUgZm9yIEluanVyZWQgUGxheWVyIiwgcGxheWVyX2lkKSwNCiAgICAgICAgeCA9ICJXZWVrIiwNCiAgICAgICAgZmlsbCA9ICJSZWxhdGl2ZSBWZWxvY2l0eSBCYW5kIg0KICAgICAgKSArDQogICAgICB0aGVtZV9jbGFzc2ljKCkNCiAgICANCiAgICBwcmludChwbG90KQ0KICAgIA0KICAgIHJldHVybihOVUxMKQ0KICB9KQ0KYGBgDQpUaGUgYmx1ZSBsaW5lcyBpbmRpY2F0ZSBhIHdlZWsgd2hlcmUgYW4gaW5qdXJ5IG9jY3VycmVkLg0KTm8gcmVhbCBpbnNpZ2h0cyBjYW4gYmUgbWFkZSBmcm9tIGxvb2tpbmcgYXQgcmVsYXRpdmUgdmVsb2NpdHkgYmFuZHMgYW5kIGluanVyeSBvY2N1cnJhbmNlcy4gDQoNCmBgYHtyfQ0KbW9kZWxfaW5qdXJ5IDwtIGdsbShpbmp1cnkgfiBwY3Rfb2ZfbWF4X3ZlbG9jaXR5LA0KICAgICAgICAgICAgICAgICAgICBkYXRhID0gaW5qdXJpZXNfYW5kX3J1bm5pbmcsDQogICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIpDQoNCnN1bW1hcnkobW9kZWxfaW5qdXJ5KQ0KYGBgDQpTaW1wbGUgbW9kZWwNCkVhY2ggMSBwZXJjZW50YWdlIHBvaW50IGluY3JlYXNlIGluIHBjdF9vZl9tYXhfdmVsb2NpdHkgaW5jcmVhc2VzIHRoZSBsb2ctb2RkcyBvZiBpbmp1cnkgYnkgMC4wMTA2Lg0KTm90IHNpZ25pZmljYW50Lg0KDQoNCmBgYHtyfQ0KIyBHZXQgc3VtbWFyeSBzdGF0cyBmb3IgYm90aCBpbmp1cnkgYW5kIG5vbiBpbmp1cnkNCg0KIyBDbGVhbiBhbmQgZ2V0IGRhdGUgZm9ybWF0DQppbmp1cmllc19hbmRfcnVubmluZ19jbGVhbiA8LSBpbmp1cmllc19hbmRfcnVubmluZyAlPiUNCiAgbXV0YXRlKHdlZWtfZm9ybWF0dGVkID0gZm9ybWF0KGFzLkRhdGUod2VlayksICIlbS0lZC0lWSIpKSAlPiUNCiAgZGlzdGluY3QoYW5vbl9pZCwgd2VlaywgLmtlZXBfYWxsID0gVFJVRSkNCg0KIyBGaWx0ZXIgZm9yIG9ubHkgaW5qdXJ5IHdlZWtzDQppbmp1cnlfd2Vla3MgPC0gaW5qdXJpZXNfYW5kX3J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcihpbmp1cnkgPT0gMSkgJT4lDQogIG11dGF0ZShpbmp1cnlfZXZlbnQgPSBwYXN0ZTAoYW5vbl9pZCwgIl9fIiwgd2Vla19mb3JtYXR0ZWQpKQ0KDQojIENhbGN1bGF0ZSBzdGF0aXN0aWNzIGZvciBpbmp1cnkgd2Vla3MNCmluanVyeV9tZWFuIDwtIG1lYW4oaW5qdXJ5X3dlZWtzJHBjdF9vZl9tYXhfdmVsb2NpdHksIG5hLnJtID0gVFJVRSkNCg0KaW5qdXJ5X3N1bW1hcnlfc3RhdHMgPC0gaW5qdXJ5X3dlZWtzICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVhbl9wY3QgPSBtZWFuKHBjdF9vZl9tYXhfdmVsb2NpdHksIG5hLnJtID0gVFJVRSksDQogICAgc2RfcGN0ID0gc2QocGN0X29mX21heF92ZWxvY2l0eSwgbmEucm0gPSBUUlVFKSwNCiAgICBuID0gc3VtKCFpcy5uYShwY3Rfb2ZfbWF4X3ZlbG9jaXR5KSkNCiAgKQ0KIyBDb25maWRlbmNlIGludGVydmFsIA0Kc2UgPC0gaW5qdXJ5X3N1bW1hcnlfc3RhdHMkc2RfcGN0IC8gc3FydChpbmp1cnlfc3VtbWFyeV9zdGF0cyRuKQ0KdF9jcml0IDwtIHF0KDAuOTc1LCBkZiA9IGluanVyeV9zdW1tYXJ5X3N0YXRzJG4gLSAxKQ0KbG93ZXIgPC0gaW5qdXJ5X3N1bW1hcnlfc3RhdHMkbWVhbl9wY3QgLSB0X2NyaXQgKiBzZQ0KdXBwZXIgPC0gaW5qdXJ5X3N1bW1hcnlfc3RhdHMkbWVhbl9wY3QgKyB0X2NyaXQgKiBzZQ0KIyBQcmludA0KY2F0KCI5NSUgQ0kgZm9yIG1lYW4gcGN0X29mX21heF92ZWxvY2l0eToiLCByb3VuZChsb3dlciwyKSwgIi0iLCByb3VuZCh1cHBlciwyKSkNCg0KDQojIEZpbHRlciBmb3Igbm9uLWluanVyeSB3ZWVrcw0Kbm9uX2luanVyeV93ZWVrcyA8LSBpbmp1cmllc19hbmRfcnVubmluZ19jbGVhbiAlPiUNCiAgZmlsdGVyKGluanVyeSA9PSAwKQ0KDQojIEdldCBzdGF0aXN0aWNzIGZvciBub24taW5qdXJ5IHdlZWtzDQpub25faW5qdXJ5X21lYW4gPC0gbWVhbihub25faW5qdXJ5X3dlZWtzJHBjdF9vZl9tYXhfdmVsb2NpdHksIG5hLnJtID0gVFJVRSkNCg0Kbm9uX2luanVyeV9zdW1tYXJ5X3N0YXRzIDwtIG5vbl9pbmp1cnlfd2Vla3MgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBtZWFuX3BjdCA9IG1lYW4ocGN0X29mX21heF92ZWxvY2l0eSwgbmEucm0gPSBUUlVFKSwNCiAgICBzZF9wY3QgPSBzZChwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCBuYS5ybSA9IFRSVUUpLA0KICAgIG4gPSBzdW0oIWlzLm5hKHBjdF9vZl9tYXhfdmVsb2NpdHkpKQ0KICApDQoNCiMgQ29uZmlkZW5jZSBpbnRlcnZhbA0Kbm9uX2luanVyeV9zZSA8LSBub25faW5qdXJ5X3N1bW1hcnlfc3RhdHMkc2RfcGN0IC8gc3FydChub25faW5qdXJ5X3N1bW1hcnlfc3RhdHMkbikNCm5vbl9pbmp1cnlfdF9jcml0IDwtIHF0KDAuOTc1LCBkZiA9IG5vbl9pbmp1cnlfc3VtbWFyeV9zdGF0cyRuIC0gMSkNCm5vbl9pbmp1cnlfbG93ZXIgPC0gbm9uX2luanVyeV9zdW1tYXJ5X3N0YXRzJG1lYW5fcGN0IC0gbm9uX2luanVyeV90X2NyaXQgKiBub25faW5qdXJ5X3NlDQpub25faW5qdXJ5X3VwcGVyIDwtIG5vbl9pbmp1cnlfc3VtbWFyeV9zdGF0cyRtZWFuX3BjdCArIG5vbl9pbmp1cnlfdF9jcml0ICogbm9uX2luanVyeV9zZQ0KDQojIFByaW50IHJlc3VsdHMNCmNhdCgiOTUlIENJIGZvciBtZWFuIHBjdF9vZl9tYXhfdmVsb2NpdHkgKG5vbi1pbmp1cnkgd2Vla3MpOiIsIA0KICAgIHJvdW5kKG5vbl9pbmp1cnlfbG93ZXIsIDIpLCAiLSIsIHJvdW5kKG5vbl9pbmp1cnlfdXBwZXIsIDIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBULXRlc3QgdG8gY29tcGFyZSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5IGluIGluanVyaWVzIGFuZCBub24gaW5qdXJpZXMgdGhhdCB3ZWVrDQp0X3Rlc3QgPC0gdC50ZXN0KA0KICBwY3Rfb2ZfbWF4X3ZlbG9jaXR5IH4gaW5qdXJ5LCANCiAgZGF0YSA9IGluanVyaWVzX2FuZF9ydW5uaW5nX2NsZWFuLA0KICB2YXIuZXF1YWwgPSBGQUxTRSAjIHVzZSBUUlVFIGlmIHlvdSBhc3N1bWUgZXF1YWwgdmFyaWFuY2VzDQopDQoNCiMgUHJpbnQgcmVzdWx0cw0KcHJpbnQodF90ZXN0KQ0KYGBgDQpHcm91cCAxIChpbmp1cnkpIG1lYW4gPSA4Ni41NQ0KR3JvdXAgMiAobm9uLWluanVyeSkgbWVhbiA9IDg1LjQ5DQpXaGlsZSB0aGUgbWVhbiAlIG9mIG1heCB2ZWxvY2l0eSBpcyBoaWdoZXIgZm9yIHRoZSBpbmp1cnkgZ3JvdXAsIHRoZSBkaWZmZXJlbmNlIGlzIHNtYWxsIGFuZCBub3Qgc3RhdGlzdGNhbGx5IHNpZ25pZmljYW50LiAoaGlnaCBwLXZhbHVlLCAwLjYpDQoNCg0KYGBge3J9ICANCiMgUGxvdCBvZiAlIE1heCBWZWxvY2l0eSBkdXJpbmcgaW5qdXJ5IGFuZCBub24gaW5qdXJ5IHdlZWtzDQpnZ3Bsb3QoaW5qdXJpZXNfYW5kX3J1bm5pbmdfY2xlYW4sIGFlcyh4ID0gd2VlaywgeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHkpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZmFjdG9yKGluanVyeSkpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIwIiA9ICJibGFjayIsICIxIiA9ICJyZWQiKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk5vIEluanVyeSIsICJJbmp1cnkiKSwNCiAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiSW5qdXJ5IFN0YXR1cyIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICIlIG9mIE1heCBWZWxvY2l0eSBieSBXZWVrIiwNCiAgICBzdWJ0aXRsZSA9IHBhc3RlKCJNZWFuICUgb2YgTWF4IFZlbG9jaXR5LCBJbmp1cnk6ICIsIHJvdW5kKGluanVyeV9tZWFuLCAyKSwgIiB8ICBNZWFuICUgb2YgTWF4IFZlbG9jaXR5LCBOb24tSW5qdXJ5OiAiLCByb3VuZChub25faW5qdXJ5X21lYW4sIDIpKSwNCiAgICB4ID0gIldlZWsiLA0KICAgIHkgPSAiJSBvZiBNYXggVmVsb2NpdHkgUmVhY2hlZCINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQoNCmdncGxvdChpbmp1cnlfd2Vla3MsIGFlcyh4ID0gd2VlaywgeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHkpKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAicmVkIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIiUgb2YgTWF4IFZlbG9jaXR5IER1cmluZyBJbmp1cnkgV2Vla3MiLA0KICAgIHN1YnRpdGxlID0gcGFzdGUoIiUgb2YgTWF4IFZlbG9jaXR5IE1lYW46ICIsIHJvdW5kKGluanVyeV9tZWFuLCAyKSksDQogICAgeCA9ICJXZWVrIiwNCiAgICB5ID0gIiUgb2YgTWF4IFZlbG9jaXR5Ig0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ3JlYXRlIGJhciBjaGFydA0KZ2dwbG90KGluanVyeV93ZWVrcywgYWVzKHggPSBmYWN0b3IoaW5qdXJ5X2V2ZW50KSwgeSA9IHBjdF9vZl9tYXhfdmVsb2NpdHkpKSArDQogIGdlb21fY29sKGZpbGwgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKHBjdF9vZl9tYXhfdmVsb2NpdHksIDEpKSwgICMgcm91bmQgdG8gMSBkZWNpbWFsIHBsYWNlDQogICAgICAgICAgICB2anVzdCA9IC0wLjUsIHNpemUgPSAyLjc1KSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiJSBvZiBNYXggVmVsb2NpdHkgRHVyaW5nIEluanVyeSBXZWVrcyIsDQogICAgc3VidGl0bGUgPSBwYXN0ZSgiTWVhbjogIiwgcm91bmQoaW5qdXJ5X21lYW4sIDIpKSwNCiAgICB4ID0gIkluanVyeSBFdmVudCIsDQogICAgeSA9ICIlIG9mIE1heCBWZWxvY2l0eSINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KYGBge3J9DQojIExvb2tpbmcgaW50byB3ZWVrcyBiZWZvcmUgaW5qdXJ5DQoNCiMgQ2FsY3VsYXRlIHBlcmNlbnQgb2YgbWF4IHZlbG9jaXR5IGZyb20gMSwgMiwgYW5kIDMgd2Vla3MgcHJpb3IgYXMgd2VsbCBhcyB0aGUgc3VtIG9mIHdlZWtzIDEgYW5kIDIgYW5kIHRoZSBzdW0gb2Ygd2Vla3MgMSwgMiwgYW5kIDMgYXMgd2VsbCBhcyB0aGUgY2hhbmdlIGJldHdlZW4gd2VlayBvbmUgYW5kIHdlZWsgMg0Kd2Vla19wcmlvciA8LSBpbmp1cmllc19hbmRfcnVubmluZ19jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIGFycmFuZ2Uod2VlaykgJT4lDQogIG11dGF0ZSgNCiAgICAjIFByZXZpb3VzIHdlZWtzIHZhbHVlcyBvZiAlIG9mIG1heCB2ZWxvY2l0eQ0KICAgIGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSA9IGxhZyhwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCAxKSwNCiAgICBsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHkgPSBsYWcocGN0X29mX21heF92ZWxvY2l0eSwgMiksDQogICAgbGFnM19wY3Rfb2ZfbWF4X3ZlbG9jaXR5ID0gbGFnKHBjdF9vZl9tYXhfdmVsb2NpdHksIDMpLA0KICAgIA0KICAgICMgQ2hhbmdlIG9mICUgb2YgbWF4IGZyb20gY3VycmVudCB3ZWVrIHRvIHByZXZpb3VzIHdlZWtzDQogICAgY2hhbmdlX2xhc3R3ZWVrID0gcGN0X29mX21heF92ZWxvY2l0eSAtIGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSwNCiAgICBjaGFuZ2VfbGFzdDJ3ZWVrcyA9IHBjdF9vZl9tYXhfdmVsb2NpdHkgLSBsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHksDQogICAgY2hhbmdlX2xhc3Qzd2Vla3MgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5IC0gbGFnM19wY3Rfb2ZfbWF4X3ZlbG9jaXR5LA0KICAgIA0KICAgICMgU3VtICUgb2YgbWF4IG9mIGN1cnJlbnQgdG8gcHJldmlvdXMgd2Vla3MNCiAgICBzdW1fbGFzdHdlZWsgPSBwY3Rfb2ZfbWF4X3ZlbG9jaXR5ICsgbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5LA0KICAgIHN1bV9sYXN0MndlZWtzID0gcGN0X29mX21heF92ZWxvY2l0eSArIGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSArIGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSwNCiAgICBzdW1fbGFzdDN3ZWVrcyA9IHBjdF9vZl9tYXhfdmVsb2NpdHkgKyBsYWcxX3BjdF9vZl9tYXhfdmVsb2NpdHkgKyBsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHkgKyBsYWczX3BjdF9vZl9tYXhfdmVsb2NpdHksDQogICAgDQogICAgIyBDaGFuZ2UgYmV0d2VlbiB3ZWVrcyAobm90IGN1cnJlbnQgd2VlaykNCiAgICBjaGFuZ2Vfd2Vla3MxXzIgPSBsYWcxX3BjdF9vZl9tYXhfdmVsb2NpdHkgLSBsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHksDQogICAgY2hhbmdlX3dlZWtzMV8zID0gbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5IC0gbGFnM19wY3Rfb2ZfbWF4X3ZlbG9jaXR5LA0KICAgIA0KICAgICMgU3VtIG9mIHdlZWtzIChub3QgY3VycmVudCB3ZWVrKQ0KICAgIHN1bV93ZWVrczFfMiA9IGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSArIGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSwNCiAgICBzdW1fd2Vla3MxXzJfMyA9IGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSArIGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSArIGxhZzNfcGN0X29mX21heF92ZWxvY2l0eQ0KICAgICkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojIEdldCBJbmp1cnkgRXZlbnQgdmFyaWFibGUgYW5kIEluanVyeSBkYXRhc2V0DQppbmp1cnlfd2Vla19wcmlvciA8LSB3ZWVrX3ByaW9yICU+JQ0KICBmaWx0ZXIoaW5qdXJ5ID09IDEpICU+JQ0KICBtdXRhdGUoaW5qdXJ5X2V2ZW50ID0gcGFzdGUwKGFub25faWQsICJfXyIsIHdlZWtfZm9ybWF0dGVkKSkNCg0KIyBHZXQgbm9uLWluanVyeSBkYXRhc2V0DQpub25faW5qdXJ5X3dlZWtfcHJpb3IgPC0gd2Vla19wcmlvciAlPiUNCiAgZmlsdGVyKGluanVyeSA9PSAwKQ0KDQojIEdldCBNZWFucw0KaW5qdXJ5X3dlZWtfcHJpb3JfbWVhbiA8LSBtZWFuKGluanVyeV93ZWVrX3ByaW9yJGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSkNCm5vbl9pbmp1cnlfd2Vla19wcmlvcl9tZWFuIDwtIG1lYW4obm9uX2luanVyeV93ZWVrX3ByaW9yJGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSkNCmluanVyeV93ZWVrX3ByaW9yX21lYW4NCm5vbl9pbmp1cnlfd2Vla19wcmlvcl9tZWFuDQoNCg0KDQojIFBsb3Qgb2YgJSBNYXggVmVsb2NpdHkgZHVyaW5nIGluanVyeSBhbmQgbm9uIGluanVyeSB3ZWVrcw0KZ2dwbG90KHdlZWtfcHJpb3IsIGFlcyh4ID0gd2VlaywgeSA9IGxhZzFfcGN0X29mX21heF92ZWxvY2l0eSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IoaW5qdXJ5KSkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIjAiID0gImJsYWNrIiwgIjEiID0gInJlZCIpLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTm8gSW5qdXJ5IiwgIkluanVyeSIpLA0KICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJJbmp1cnkgU3RhdHVzIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIiUgb2YgTWF4IFZlbG9jaXR5IG9mIFByaW9yIFdlZWsiLA0KICAgIHN1YnRpdGxlID0gcGFzdGUoIk1lYW4gJSBvZiBNYXggVmVsb2NpdHksIEluanVyeTogIiwgcm91bmQoaW5qdXJ5X3dlZWtfcHJpb3JfbWVhbiwgMiksICIgfCAgTWVhbiAlIG9mIE1heCBWZWxvY2l0eSwgTm9uLUluanVyeTogIiwgcm91bmQobm9uX2luanVyeV93ZWVrX3ByaW9yX21lYW4sIDIpKSwNCiAgICB4ID0gIldlZWsiLA0KICAgIHkgPSAiJSBvZiBNYXggVmVsb2NpdHkgUmVhY2hlZCINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQoNCmdncGxvdChpbmp1cnlfd2Vla19wcmlvciwgYWVzKHggPSB3ZWVrLCB5ID0gbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5KSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gInJlZCIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICIlIG9mIE1heCBWZWxvY2l0eSB0aGUgd2VlayBiZWZvcmUgaW5qdXJ5IiwNCiAgICBzdWJ0aXRsZSA9IHBhc3RlKCIlIG9mIE1heCBWZWxvY2l0eSBNZWFuOiAiLCByb3VuZChpbmp1cnlfd2Vla19wcmlvcl9tZWFuLCAyKSksDQogICAgeCA9ICJXZWVrIiwNCiAgICB5ID0gIiUgb2YgTWF4IFZlbG9jaXR5Ig0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQmFyIGNoYXJ0IGZvciB3ZWVrIHByaW9yIHRvIGluanVyeSAlIE1heA0KZ2dwbG90KGluanVyeV93ZWVrX3ByaW9yLCBhZXMoeCA9IGZhY3Rvcihpbmp1cnlfZXZlbnQpLCB5ID0gbGFnMV9wY3Rfb2ZfbWF4X3ZlbG9jaXR5KSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiNDRkI4N0MiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChwY3Rfb2ZfbWF4X3ZlbG9jaXR5LCAxKSksICAjIHJvdW5kIHRvIDEgZGVjaW1hbCBwbGFjZQ0KICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBzaXplID0gMi43NSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIiUgb2YgTWF4IFZlbG9jaXR5IHRoZSBXZWVrIEJlZm9yZSBJbmp1cnkiLA0KICAgIHN1YnRpdGxlID0gcGFzdGUoIiUgTWF4IE1lYW46ICIsIHJvdW5kKGluanVyeV93ZWVrX3ByaW9yX21lYW4sIDIpKSwNCiAgICB4ID0gIkluanVyeSBFdmVudCIsDQogICAgeSA9ICIlIG9mIE1heCBWZWxvY2l0eSINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBULXRlc3QgdG8gY29tcGFyZSBsYWdfcGN0X29mX21heF92ZWxvY2l0eSAodGhlIHdlZWsgYmVmb3JlKSBiZXR3ZWVuIGluanVyeSBhbmQgbm9uIGluanVyeQ0KdF90ZXN0X3dlZWtfcHJpb3IgPC0gdC50ZXN0KA0KICBsYWcxX3BjdF9vZl9tYXhfdmVsb2NpdHkgfiBpbmp1cnksIA0KICBkYXRhID0gd2Vla19wcmlvciwNCiAgdmFyLmVxdWFsID0gRkFMU0UpDQpwcmludCh0X3Rlc3Rfd2Vla19wcmlvcikNCg0KDQojIFQtdGVzdCB0byBjb21wYXJlIGNoYW5nZSBiZXR3ZWVuIGN1cnJlbnQgd2VlayBhbmQgbGFzdCB3ZWVrDQp0X3Rlc3RfY2hhbmdlX2xhc3R3ZWVrIDwtIHQudGVzdCgNCiAgY2hhbmdlX2xhc3R3ZWVrIH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0X2NoYW5nZV9sYXN0d2VlaykNCg0KIyBULXRlc3QgdG8gY29tcGFyZSBjaGFuZ2UgYmV0d2VlbiBjdXJyZW50IHdlZWsgYW5kIDIgd2Vla3MgYWdvDQp0X3Rlc3RfY2hhbmdlX2xhc3Qyd2Vla3MgPC0gdC50ZXN0KA0KICBjaGFuZ2VfbGFzdDJ3ZWVrcyB+IGluanVyeSwgDQogIGRhdGEgPSB3ZWVrX3ByaW9yLA0KICB2YXIuZXF1YWwgPSBGQUxTRSkNCnByaW50KHRfdGVzdF9jaGFuZ2VfbGFzdDJ3ZWVrcykNCg0KIyBULXRlc3QgdG8gY29tcGFyZSBjaGFuZ2UgYmV0d2VlbiBjdXJyZW50IHdlZWsgYW5kIDMgd2Vla3MgYWdvDQp0X3Rlc3RfY2hhbmdlX2xhc3Qzd2Vla3MgPC0gdC50ZXN0KA0KICBjaGFuZ2VfbGFzdDN3ZWVrcyB+IGluanVyeSwgDQogIGRhdGEgPSB3ZWVrX3ByaW9yLA0KICB2YXIuZXF1YWwgPSBGQUxTRSkNCnByaW50KHRfdGVzdF9jaGFuZ2VfbGFzdDN3ZWVrcykNCg0KDQojIFQtdGVzdCB0byBjb21wYXJlIHN1bSBvZiBjdXJyZW50IHdlZWssIDEsIDIsIGFuZCAzIHdlZWtzIGJlZm9yZQ0KdF90ZXN0X3N1bV9sYXN0M3dlZWtzIDwtIHQudGVzdCgNCiAgc3VtX2xhc3Qzd2Vla3MgfiBpbmp1cnksIA0KICBkYXRhID0gd2Vla19wcmlvciwNCiAgdmFyLmVxdWFsID0gRkFMU0UpDQpwcmludCh0X3Rlc3Rfc3VtX2xhc3Qzd2Vla3MpDQpgYGANCk1lYW4gJSBvZiBtYXggd2VlayBiZWZvcmU6IGluanVyeSA9IDg2LjYsIG5vbi1pbmp1cnkgPSA4NS45Lg0KTm90IHNpZyAocHZhbHVlID0gMC43OSkNCg0KTWVhbiAlIGNoYW5nZSB3ZWVrIGJlZm9yZSAoY3VycmVudCB3ZWVrICUgLSBsYXN0IHdlZWsgJSk6IGluanVyeSA9IC0wLjA1Nywgbm9uLWluanVyeSA9IC0wLjQ5DQpOb3Qgc2lnIChwLXZhbHVlID0gMC44OSkNCg0KTWVhbiAlIGNoYW5nZSBvZiBsYXN0IDIgd2Vla3MgKGN1cnJlbnQgLSAyIHdlZWtzIGFnbyk6IGluanVyeSA9IC0yLjY4LCBub24taW5qdXJ5ID0gLTAuNjUNCk5vdCBzaWcsIHAtdmFsdWUgPSAwLjQ0DQoNCk1lYW4gJSBjaGFuZ2Ugb2YgbGFzdCAzIHdlZWtzIChjdXJyZW50IC0gMyB3ZWVrcyBhZ28pOiBpbmp1cnkgPSAtMC42LCBub24taW5qdXJ5ID0gLTAuOC4NCk5vdCBzaWcsIHAtdmFsdWUgPSAwLjk1DQoNCmBgYHtyfQ0KIyBMb29rIGludG8gdGhlIGxhc3QgdHdvIHdlZWtzDQoNCiMgRHJvcCBOQXMNCmxhZ18yX2luanVyeSA8LSBpbmp1cnlfd2Vla19wcmlvciAlPiUNCiAgZHJvcF9uYShsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHkpDQpsYWdfMl9ub25faW5qdXJ5IDwtIG5vbl9pbmp1cnlfd2Vla19wcmlvciAlPiUNCiAgZHJvcF9uYShsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHkpDQoNCiMgQ2FsY3VsYXRlIG1lYW5zDQptZWFuX2xhZzJfaW5qdXJ5IDwtIG1lYW4obGFnXzJfaW5qdXJ5JGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSkNCm1lYW5fbGFnMl9ub25faW5qdXJ5IDwtIG1lYW4obGFnXzJfbm9uX2luanVyeSRsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHkpDQptZWFuX2xhZzJfaW5qdXJ5DQptZWFuX2xhZzJfbm9uX2luanVyeQ0KDQojIFR3byB3ZWVrcyBiZWZvcmU6DQojIFQtdGVzdCB0byBjb21wYXJlIGxhZzJfcGN0X29mX21heF92ZWxvY2l0eSAoMiB3ZWVrcyBiZWZvcmUpIGJldHdlZW4gaW5qdXJ5IGFuZCBub24gaW5qdXJ5DQp0X3Rlc3RfMndlZWtfcHJpb3IgPC0gdC50ZXN0KA0KICBsYWcyX3BjdF9vZl9tYXhfdmVsb2NpdHkgfiBpbmp1cnksIA0KICBkYXRhID0gd2Vla19wcmlvciwNCiAgdmFyLmVxdWFsID0gRkFMU0UpDQpwcmludCh0X3Rlc3RfMndlZWtfcHJpb3IpDQoNCiMgQ2hhbmdlIG9mIGxhc3QgdHdvIHdlZWtzIChsYWcxIC0gbGFnMikNCnRfdGVzdF9jaGFuZ2Vfd2Vla3MxXzIgPC0gdC50ZXN0KA0KICBjaGFuZ2Vfd2Vla3MxXzIgfiBpbmp1cnksIA0KICBkYXRhID0gd2Vla19wcmlvciwNCiAgdmFyLmVxdWFsID0gRkFMU0UpDQpwcmludCh0X3Rlc3RfY2hhbmdlX3dlZWtzMV8yKQ0KDQojIFN1bSBvZiB3ZWVrcyAxIGFuZCAyDQp0X3Rlc3Rfc3VtX3dlZWtzMV8yIDwtIHQudGVzdCgNCiAgc3VtX3dlZWtzMV8yIH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0X3N1bV93ZWVrczFfMikNCg0KDQojIDMgd2Vla3MgYmVmb3JlDQojIFQtdGVzdCB0byBjb21wYXJlIGxhZzNfcGN0X29mX21heF92ZWxvY2l0eSAoMyB3ZWVrcyBiZWZvcmUpIGJldHdlZW4gaW5qdXJ5IGFuZCBub24gaW5qdXJ5DQp0X3Rlc3RfM3dlZWtfcHJpb3IgPC0gdC50ZXN0KA0KICBsYWczX3BjdF9vZl9tYXhfdmVsb2NpdHkgfiBpbmp1cnksIA0KICBkYXRhID0gd2Vla19wcmlvciwNCiAgdmFyLmVxdWFsID0gRkFMU0UpDQpwcmludCh0X3Rlc3RfM3dlZWtfcHJpb3IpDQoNCiMgQ2hhbmdlIGJldHdlZW4gMSB3ZWVrcyBiZWZvcmUgYW5kIDMgd2Vla3MgYmVmb3JlDQp0X3Rlc3RfY2hhbmdlX3dlZWtzMV8zIDwtIHQudGVzdCgNCiAgY2hhbmdlX3dlZWtzMV8zIH4gaW5qdXJ5LCANCiAgZGF0YSA9IHdlZWtfcHJpb3IsDQogIHZhci5lcXVhbCA9IEZBTFNFKQ0KcHJpbnQodF90ZXN0X2NoYW5nZV93ZWVrczFfMykNCg0KIyBTdW0gb2YgMSwgMiwgYW5kIDMgd2Vla3MgYmVmb3JlIGluanVyeQ0KdF90ZXN0X3N1bV93ZWVrczFfMl8zIDwtIHQudGVzdCgNCiAgc3VtX3dlZWtzMV8yXzMgfiBpbmp1cnksIA0KICBkYXRhID0gd2Vla19wcmlvciwNCiAgdmFyLmVxdWFsID0gRkFMU0UpDQpwcmludCh0X3Rlc3Rfc3VtX3dlZWtzMV8yXzMpDQpgYGANCjIgd2Vla3MgYmVmb3JlIGluanVyeToNCk1lYW5zIG9mICUgb2YgbWF4OiBpbmp1cnkgPSA4OC43LCBub24taW5qdXJ5ID0gODUuOA0KTm90IGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBtZWFucywgcC12YWx1ZSwgMC4xODMzID4gMC4wNS4NCg0KTWVhbnMgb2YgY2hhbmdlOiBpbmp1cnkgPSAtMi4zNCwgbm9uLWluanVyeSA9IDAuMTA4DQpOb3Qgc2lnbmlmaWNhbnQgKHAtdmFsdWUgPSAwLjQ3KQ0KDQpNZWFuIG9mIHN1bSBvZiB3ZWVrcyAxIGFuZCAyOiBpbmp1cnkgPSAxNzUuMTQsIG5vbi1pbmp1cnkgPSAxNzEuNzINCk5vdCBzaWduaWZpY2FudCwgcC12YWx1ZSA9IDAuMzMzDQoNCg0KMyB3ZWVrcyBiZWZvcmUgaW5qdXJ5Og0KTWVhbnMgb2YgJSBvZiBtYXg6IGluanVyeSA9IDg2LjcsIG5vbi1pbmp1cnkgPSA4NS45DQpOb3QgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIG1lYW5zLCBoaWdoIHAtdmFsdWUsIDAuNw0KDQpNZWFuIHN1bSBvZiAxLCAyLCBhbmQgMyB3ZWVrcyBiZWZvcmUgaW5qdXJ5OiBpbmp1cnkgPSAyNjEuOCwgbm9uLWluanVyeSA9IDI1Ny4zDQpOb3Qgc2lnLCBwLXZhbHVlID0gMC4zNg0KDQpNZWFuIGNoYW5nZSBiZXR3ZWVuIDEgYW5kIDMgd2Vla3MgYmVmb3JlOiBpbmp1cnkgPSAtMC4yNiwgbm9uLWluanVyeSA9IC0wLjI3DQpOb3Qgc2lnLCBwLXZhbCA9IDAuOTkNCg0KYGBge3J9DQptb2RlbCA8LSBnbG0oaW5qdXJ5IH4gY2hhbmdlX2xhc3R3ZWVrICsgY2hhbmdlX2xhc3Qyd2Vla3MsDQogICAgICAgICAgICAgZGF0YSA9IHdlZWtfcHJpb3IsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQpUaGVyZSBpcyBubyBjbGVhciBldmlkZW5jZSB0aGF0ICUgb2YgbWF4IHZlbG9jaXR5IGluIHRoZSBjdXJyZW50IG9yIHByaW9yIHdlZWtzIChvciB0aGVpciBjaGFuZ2VzL3N1bXMpIGRpZmZlcnMgYmV0d2VlbiBpbmp1cnkgYW5kIG5vbi1pbmp1cnkgd2Vla3MsIGJhc2VkIG9uIHQtdGVzdHMgb2YgbWVhbiBkaWZmZXJlbmNlcy4NCg0KIyMjIyMgQ29tcGFyaW5nIHRoZSBudW1iZXIgb2YgPjkwJSBlZmZvcnRzIHdpdGggaW5qdXJ5DQoNCmBgYHtyfQ0KIyBDb252ZXJ0IDkwIHBlcmNlbnQgbWF4IHRvIGxvZ2ljYWwNCiMgSW5zdGVhZCBvZiBZZXMvTm8gaXQgaXMgVHJ1ZS9GYWxzZQ0KY2F0YXB1bHRfd2Vla2x5IDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIG11dGF0ZSgNCiAgICBEYXRlID0gYXMuRGF0ZShEYXRlKSwNCiAgICB3ZWVrID0gZmxvb3JfZGF0ZShEYXRlLCB1bml0ID0gIndlZWsiKSwNCiAgICBIaXQ5MCA9IEhpdC45MC5QZXJjZW50Lk1heCA9PSAiWWVzIiAgIyBDb252ZXJ0IHRvIGxvZ2ljYWwNCiAgKSAlPiUNCiAgZGlzdGluY3QoYW5vbl9pZCwgRGF0ZSwgLmtlZXBfYWxsID0gVFJVRSkNCg0KDQojIEdldCBhbGwgY29tYmluYXRpb25zIG9mIHBsYXllciBhbmQgd2Vlaw0KYWxsX2NvbWJpbmF0aW9ucyA8LSBjYXRhcHVsdF93ZWVrbHkgJT4lDQogIGRpc3RpbmN0KGFub25faWQsIHdlZWspDQoNCiMgQ291bnQgd2Vla2x5ID45MCUgc3ByaW50IGhpdHMNCndlZWtseV9oaXRzIDwtIGNhdGFwdWx0X3dlZWtseSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgd2VlaykgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBjb3VudF85MHBjdCA9IHN1bShIaXQ5MCwgbmEucm0gPSBUUlVFKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkNCg0KIyBKb2luIHdpdGggYWxsIGNvbWJpbmF0aW9ucyB0byBpbmNsdWRlIHplcm8gY291bnRzDQp3ZWVrbHlfOTBwY3QgPC0gYWxsX2NvbWJpbmF0aW9ucyAlPiUNCiAgbGVmdF9qb2luKHdlZWtseV9oaXRzLCBieSA9IGMoImFub25faWQiLCAid2VlayIpKSAlPiUNCiAgbXV0YXRlKGNvdW50XzkwcGN0ID0gcmVwbGFjZV9uYShjb3VudF85MHBjdCwgMCkpDQoNCmRpc3RydWJ1dGlvbiA8LSB3ZWVrbHlfOTBwY3QgJT4lDQogIGNvdW50KGNvdW50XzkwcGN0KSAlPiUNCiAgYXJyYW5nZShkZXNjKG4pKSAgIyBPcHRpb25hbDogc29ydCBmcm9tIG1vc3QgdG8gbGVhc3QgY29tbW9uDQpgYGANCg0KYGBge3J9DQojIENyZWF0ZSBpbmp1cnkgd2Vla3MNCmluanVyeV93ZWVrcyA8LSBJbmNpZGVudF9SZXBvcnRfY2xlYW4gJT4lDQogIG11dGF0ZSgNCiAgICB3ZWVrID0gZmxvb3JfZGF0ZShhcy5EYXRlKERhdGUub2YuSW5qdXJ5KSwgdW5pdCA9ICJ3ZWVrIikNCiAgKSAlPiUNCiAgZmlsdGVyKERhdGUub2YuSW5qdXJ5ID49IGFzLkRhdGUoIjIwMjQtMDYtMzAiKSAmIERhdGUub2YuSW5qdXJ5IDw9IGFzLkRhdGUoIjIwMjUtMDctMDEiKSkgJT4lDQogIHNlbGVjdChhbm9uX2lkLCB3ZWVrKSAlPiUNCiAgZGlzdGluY3QoKSAlPiUNCiAgbXV0YXRlKGluanVyeSA9IDEpDQoNCiMgTWVyZ2Ugd2l0aCBpbmp1cnkgZGF0YSB0byBoYXZlIGNvbHVtbiB0aGF0IGluZGljYXRlcyBpbmp1cnkgd2Vlaw0Kd2Vla2x5XzkwcGN0X2luanVyaWVzIDwtIHdlZWtseV85MHBjdCAlPiUNCiAgbGVmdF9qb2luKGluanVyeV93ZWVrcywgYnkgPSBjKCJhbm9uX2lkIiwgIndlZWsiKSkgJT4lDQogIG11dGF0ZShpbmp1cnkgPSByZXBsYWNlX25hKGluanVyeSwgMCkpDQoNCiMgQ2FsY3VsYXRlIGxhZ2dlZCA+OTAlIGVmZm9ydCBjb3VudHMNCndlZWtseV85MHBjdF9pbmp1cmllcyA8LSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgJT4lDQogIGFycmFuZ2UoYW5vbl9pZCwgd2VlaykgJT4lDQogIGdyb3VwX2J5KGFub25faWQpICU+JQ0KICBtdXRhdGUoDQogICAgbGFnMV85MHBjdF9jb3VudCA9IGxhZyhjb3VudF85MHBjdCwgMSksDQogICAgbGFnMl85MHBjdF9jb3VudCA9IGxhZyhjb3VudF85MHBjdCwgMiksDQogICAgbGFnM185MHBjdF9jb3VudCA9IGxhZyhjb3VudF85MHBjdCwgMyksDQogICAgDQogICAgY2hhbmdlX2xhc3R3ZWVrID0gY291bnRfOTBwY3QgLSBsYWcxXzkwcGN0X2NvdW50LA0KICAgIGNoYW5nZV9sYXN0MndlZWtzID0gY291bnRfOTBwY3QgLSBsYWcyXzkwcGN0X2NvdW50LA0KICAgIGNoYW5nZV9sYXN0M3dlZWtzID0gY291bnRfOTBwY3QgLSBsYWczXzkwcGN0X2NvdW50LA0KICAgIA0KICAgIHN1bV9sYXN0MndlZWtzID0gY291bnRfOTBwY3QgKyBsYWcxXzkwcGN0X2NvdW50LA0KICAgIHN1bV9sYXN0M3dlZWtzID0gY291bnRfOTBwY3QgKyBsYWcxXzkwcGN0X2NvdW50ICsgbGFnMl85MHBjdF9jb3VudCwNCiAgICANCiAgICBhdmdfbGFzdHdlZWsgPSAoY291bnRfOTBwY3QgKyBsYWcxXzkwcGN0X2NvdW50KSAvIDIsDQogICAgYXZnX2xhc3Qyd2Vla3MgPSAoY291bnRfOTBwY3QgKyBsYWcxXzkwcGN0X2NvdW50ICsgbGFnMl85MHBjdF9jb3VudCkgLyAzLA0KICAgIGF2Z19sYXN0M3dlZWtzID0gKGNvdW50XzkwcGN0ICsgbGFnMV85MHBjdF9jb3VudCArIGxhZzJfOTBwY3RfY291bnQgKyBsYWczXzkwcGN0X2NvdW50KSAvIDQNCiAgKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3J9DQojIEJhciBjaGFydCBvZiBzcHJpbnQgY291bnRzIHRoZSB3ZWVrIG9mIGFuIGluanVyeQ0KDQojIE9ubHkgaW5qdXJpZXMNCmluanVyeV9zdW1tYXJ5IDwtIHdlZWtseV85MHBjdF9pbmp1cmllcyAlPiUNCiAgZ3JvdXBfYnkoY291bnRfOTBwY3QpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgdG90YWxfaW5qdXJpZXMgPSBzdW0oaW5qdXJ5KSwNCiAgICB0b3RhbF93ZWVrcyA9IG4oKQ0KICApDQoNCmdncGxvdChpbmp1cnlfc3VtbWFyeSwgYWVzKHggPSBmYWN0b3IoY291bnRfOTBwY3QpLCB5ID0gdG90YWxfaW5qdXJpZXMpKSArDQogIGdlb21fY29sKGZpbGwgPSAiI0NGQjg3QyIpICsNCiAgbGFicygNCiAgICB4ID0gIldlZWtseSA+OTAlIFNwcmludCBDb3VudCIsDQogICAgeSA9ICJUb3RhbCBJbmp1cmllcyIsDQogICAgdGl0bGUgPSAiVG90YWwgSW5qdXJ5IFdlZWtzIGJ5IFdlZWtseSBTcHJpbnQgQ291bnQiDQogICkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBJbmp1cnkgcmF0ZSB0YWJsZSBmb3IgY3VycmVudCB3ZWVrIGNvdW50cw0KaW5qdXJ5X3JhdGVfdGFibGUgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBncm91cF9ieShjb3VudF85MHBjdCkgJT4lICAgICAgICAgICAgICAgICAgICAgICMgR3JvdXAgYnkgc3ByaW50IGNvdW50DQogIHN1bW1hcmlzZSgNCiAgICB0b3RhbF93ZWVrcyA9IG4oKSwgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRvdGFsIHdlZWtzIHdpdGggdGhpcyBjb3VudA0KICAgIGluanVyeV93ZWVrcyA9IHN1bShpbmp1cnkgPT0gMSksICAgICAgICAgICAgICMgSG93IG1hbnkgaGFkIGluanVyeSB0aGF0IHdlZWsNCiAgICBpbmp1cnlfcmF0ZSA9IGluanVyeV93ZWVrcyAvIHRvdGFsX3dlZWtzICAgICMgUHJvcG9ydGlvbiBpbmp1cmVkDQogICkgJT4lDQogIGFycmFuZ2UoZGVzYyhpbmp1cnlfcmF0ZSkpDQppbmp1cnlfcmF0ZV90YWJsZQ0KDQojIFRhYmxlIGZvciBjb3VudHMgb3ZlciBsYXN0IGZldyB3ZWVrcw0Kd2Vla2x5XzkwcGN0X2luanVyaWVzIDwtIHdlZWtseV85MHBjdF9pbmp1cmllcyAlPiUNCiAgbXV0YXRlKGF2ZzJfZ3JvdXAgPSByb3VuZChhdmdfbGFzdDJ3ZWVrcywgMSkpDQoNCiMgQ3JlYXRlIGJpbm5lZCBncm91cHM6IExvdyAoMOKAkzEpLCBNb2RlcmF0ZSAoMeKAkzIpLCBIaWdoICgyKykNCndlZWtseV85MHBjdF9pbmp1cmllcyA8LSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgJT4lDQogIG11dGF0ZShsb2FkX2dyb3VwID0gY2FzZV93aGVuKA0KICAgIGF2Z19sYXN0MndlZWtzIDwgMSB+ICJMb3cgKDwxKSIsDQogICAgYXZnX2xhc3Qyd2Vla3MgPj0gMSAmIGF2Z19sYXN0MndlZWtzIDwgMiB+ICJNb2RlcmF0ZSgxLTIpIiwNCiAgICBhdmdfbGFzdDJ3ZWVrcyA+PSAyIH4gIkhpZ2ggKDIrKSINCiAgKSkNCg0KIyBDcmVhdGUgYSB0YWJsZSBvZiBjb3VudHMgYW5kIGluanVyeSByYXRlcyBieSBncm91cA0KaW5qdXJ5X3JhdGVfYnlfZ3JvdXAgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBncm91cF9ieShsb2FkX2dyb3VwKSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIHRvdGFsX3dlZWtzID0gbigpLA0KICAgIGluanVyeV93ZWVrcyA9IHN1bShpbmp1cnkgPT0gMSksDQogICAgaW5qdXJ5X3JhdGUgPSBpbmp1cnlfd2Vla3MgLyB0b3RhbF93ZWVrcw0KICApICU+JQ0KICBhcnJhbmdlKGxvYWRfZ3JvdXApICAjIG9wdGlvbmFsOiBvcmRlciBMb3csIE1vZGVyYXRlLCBIaWdoDQppbmp1cnlfcmF0ZV9ieV9ncm91cA0KYGBgDQpDdXJyZW50IHdlZWsgY291bnRzOg0KSW5qdXJpZXMgaW5jcmVhc2Ugc2xpZ2h0bHkgZnJvbSAwLTMgY291bnRzIHdpdGggMyBoYXZpbmcgdGhlIGhpZ2hlc3QgaW5qdXJ5IHJhdGUgb2YgMS4xNjMlLiA0IGFuZCA1IGNvdW50cyBoYXZlIG5vIGluanVyaWVzIGJ1dCBhIGxvdyBzYW1wbGUgc2l6ZS4NCg0KQ291bnRzIG92ZXIgbGFzdCBmZXcgd2Vla3MgKEdyb3VwZWQpOg0KSGlnaCBjb3VudHMgaGFkIG5vIGluanVyaWVzDQpNb2RlcmF0ZSBjb3VudHMgaGFkIHRoZSBoaWdoZXN0IGluanVyeSByYXRlICgwLjk2JSkNCkxvdyBjb3VudHMgaGFkIGEgaW5qdXJ5IHJhdGUgb2YgMC42NSUNCg0KSW5qdXJ5IHJhdGVzIGluY3JlYXNlZCBmcm9tIGxvdyBjb3VudHMgKDwxKSBpbmp1cnkgcmF0ZSBvZiAwLjY1JSB0byBtb2RlcmF0ZSBjb3VudHMgKDEtMikgaW5qdXJ5IHJhdGUgb2YgMC45NiUgdGhlbiBkcm9wcGVkIHRvIDAlIGZvciBoaWdoIGNvdW50cyAoMispLiBIb3dldmVyLCBoaWdoIGNvdW50cyBoYXZlIGEgc21hbGwgc2FtcGxlIHNpemUgKDE0MyksIHNvIHRoZSByYXRlIG1heSBiZSB1bnN0YWJsZS4NCg0KYGBge3J9DQojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlIG1hbnVhbGx5DQppbmp1cnlfdGFibGUgPC0gbWF0cml4KGMoDQogIDExLCAxNjk5LCAgICMgTG93OiBpbmp1cmVkLCBub3QgaW5qdXJlZA0KICA2LCA2MjcsICAgICAjIE1vZGVyYXRlOiBpbmp1cmVkLCBub3QgaW5qdXJlZA0KICAwLCAxNDMgICAgICAjIEhpZ2g6IGluanVyZWQsIG5vdCBpbmp1cmVkDQopLCBucm93ID0gMywgYnlyb3cgPSBUUlVFKQ0KDQojIEFkZCByb3cgYW5kIGNvbHVtbiBuYW1lcyBmb3IgY2xhcml0eQ0Kcm93bmFtZXMoaW5qdXJ5X3RhYmxlKSA8LSBjKCJMb3ciLCAiTW9kZXJhdGUiLCAiSGlnaCIpDQpjb2xuYW1lcyhpbmp1cnlfdGFibGUpIDwtIGMoIkluanVyZWQiLCAiTm90X0luanVyZWQiKQ0KDQojIFJ1biBGaXNoZXIncyBFeGFjdCBUZXN0DQpmaXNoZXJfcmVzdWx0IDwtIGZpc2hlci50ZXN0KGluanVyeV90YWJsZSkNCg0KIyBWaWV3IHRoZSByZXN1bHQNCnByaW50KGZpc2hlcl9yZXN1bHQpDQpgYGANCk91ciBwLXZhbHVlIG9mIDAuNTYgbWVhbnMgdGhhdCB0aGVyZSBpcyBubyBzdGF0aWNhbGx5IHNpZ25pZmljYW5jZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIG91ciBzcHJpbnQgbG9hZCBncm91cHMgYW5kIGluanVyeS4gDQpJbmp1cnkgZXZlbnRzIGFyZSBwcmV0dHkgcmFyZSBvdmVyYWxsIGluIG91ciBkYXRhc2V0LCBtYWtpbmcgaXQgaGFyZCB0byBkZXRlY3Qgc3VidGxlIGRpZmZlcmVuY2VzLg0KDQpgYGB7cn0NCiMgVC10ZXN0czoNCg0KIyBDdXJyZW50IHdlZWsNCnQudGVzdChjb3VudF85MHBjdCB+IGluanVyeSwgZGF0YSA9IHdlZWtseV85MHBjdF9pbmp1cmllcywgdmFyLmVxdWFsID0gRkFMU0UpDQoNCiMgV2VlayBiZWZvcmUNCnQudGVzdChsYWcxXzkwcGN0X2NvdW50IH4gaW5qdXJ5LCBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLCB2YXIuZXF1YWwgPSBGQUxTRSkNCg0KIyBDaGFuZ2UgbGFzdCB3ZWVrIHRvIHRoaXMgd2Vlaw0KdC50ZXN0KGNoYW5nZV9sYXN0d2VlayB+IGluanVyeSwgZGF0YSA9IHdlZWtseV85MHBjdF9pbmp1cmllcywgdmFyLmVxdWFsID0gRkFMU0UpDQoNCiMgQ2hhbmdlIGxhc3QgdHdvIHdlZWtzDQp0LnRlc3QoY2hhbmdlX2xhc3Qyd2Vla3MgfiBpbmp1cnksIGRhdGEgPSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMsIHZhci5lcXVhbCA9IEZBTFNFKQ0KDQojIFN1bSBvdmVyIGxhc3QgMiB3ZWVrcw0KdC50ZXN0KHN1bV9sYXN0MndlZWtzIH4gaW5qdXJ5LCBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLCB2YXIuZXF1YWwgPSBGQUxTRSkNCg0KDQpgYGANCg0KYGBge3J9DQp3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBtdXRhdGUoDQogICAgc3ByaW50X2V4cG9zdXJlX2JpbiA9IGlmZWxzZShjb3VudF85MHBjdCA+IDAsICJTb21lIiwgIk5vbmUiKQ0KICApDQoNCnRhYmxlX2V4cG9zdXJlIDwtIHRhYmxlKHdlZWtseV85MHBjdF9pbmp1cmllcyRzcHJpbnRfZXhwb3N1cmVfYmluLCB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMkaW5qdXJ5KQ0KDQojIEZpc2hlcidzIEV4YWN0IFRlc3QNCmZpc2hlci50ZXN0KHRhYmxlX2V4cG9zdXJlKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIHRvcCBxdWFydGlsZSBleHBvc3VyZQ0KY3V0b2ZmIDwtIHF1YW50aWxlKHdlZWtseV85MHBjdF9pbmp1cmllcyRjb3VudF85MHBjdCwgMC43NSwgbmEucm0gPSBUUlVFKQ0KDQp3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgPC0gd2Vla2x5XzkwcGN0X2luanVyaWVzICU+JQ0KICBtdXRhdGUoDQogICAgc3ByaW50X2V4cG9zdXJlX2dyb3VwID0gaWZlbHNlKGNvdW50XzkwcGN0ID49IGN1dG9mZiwgIkhpZ2giLCAiTG93IikNCiAgKQ0KDQp0YWJsZV9xdWFydGlsZSA8LSB0YWJsZSh3ZWVrbHlfOTBwY3RfaW5qdXJpZXMkc3ByaW50X2V4cG9zdXJlX2dyb3VwLCB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMkaW5qdXJ5KQ0KDQpmaXNoZXIudGVzdCh0YWJsZV9xdWFydGlsZSkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2xtKGluanVyeSB+IGNvdW50XzkwcGN0LCBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0KDQpnbG0oaW5qdXJ5IH4gbGFnMV85MHBjdF9jb3VudCwgZGF0YSA9IHdlZWtseV85MHBjdF9pbmp1cmllcywgZmFtaWx5ID0gImJpbm9taWFsIikNCg0KY2hhbmdlIDwtIGdsbShpbmp1cnkgfiBjaGFuZ2VfbGFzdHdlZWssIGRhdGEgPSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQpzdW1tYXJ5KGNoYW5nZSkNCmdsbShpbmp1cnkgfiBjaGFuZ2VfbGFzdDJ3ZWVrcywgZGF0YSA9IHdlZWtseV85MHBjdF9pbmp1cmllcywgZmFtaWx5ID0gImJpbm9taWFsIikNCg0KDQpzdW0gPC0gZ2xtKGluanVyeSB+IHN1bV9sYXN0M3dlZWtzLCBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0Kc3VtbWFyeShzdW0pDQpgYGANCg0KYGBge3J9DQptdWx0aV9tb2RlbCA8LSBnbG0oDQogIGluanVyeSB+IGNvdW50XzkwcGN0ICsgc3VtX2xhc3Qyd2Vla3MsDQogIGRhdGEgPSB3ZWVrbHlfOTBwY3RfaW5qdXJpZXMsDQogIGZhbWlseSA9ICJiaW5vbWlhbCINCikNCg0Kc3VtbWFyeShtdWx0aV9tb2RlbCkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KHdlZWtseV85MHBjdF9pbmp1cmllcywgYWVzKHggPSBjaGFuZ2VfbGFzdHdlZWssIHkgPSBpbmp1cnkpKSArDQogIGdlb21faml0dGVyKGhlaWdodCA9IDAuMDUsIGFscGhhID0gMC4zLCBjb2xvciA9ICJibHVlIikgKyAgIyBKaXR0ZXIgdG8gc3ByZWFkIHBvaW50cyB2ZXJ0aWNhbGx5DQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnbG0iLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksIHNlID0gVFJVRSwgY29sb3IgPSAicmVkIikgKyAgIyBMb2dpc3RpYyByZWdyZXNzaW9uIGN1cnZlDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUHJvYmFiaWxpdHkgb2YgSW5qdXJ5IHZzIENoYW5nZSBpbiA+OTAlIFNwcmludCBDb3VudHMgKExhc3QgV2VlaykiLA0KICAgIHggPSAiQ2hhbmdlIGluID45MCUgU3ByaW50IENvdW50cyAoQ3VycmVudCBXZWVrIC0gUHJldmlvdXMgV2VlaykiLA0KICAgIHkgPSAiSW5qdXJ5ICgwID0gTm8sIDEgPSBZZXMpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KTm8gdC10ZXN0cyBvciBtb2RlbHMgcmV2ZWFsZWQgYW55IGluc2lnaHRzLiANCg0KU2VwYXJhdGUgc3ByaW50IGNvdW50cyBpbnRvIGdyb3Vwcw0KDQoNCmBgYHtyfQ0KIyBDcmVhdGUgZ3JvdXBzIGZvciBudW1iZXIgb2Ygc3ByaW50IGNvdW50cw0KDQpzdW1tYXJ5KHdlZWtseV85MHBjdF9pbmp1cmllcyRjb3VudF85MHBjdCkNCg0Kd2Vla2x5XzkwcGN0X2luanVyaWVzIDwtIHdlZWtseV85MHBjdF9pbmp1cmllcyAlPiUNCiAgbXV0YXRlKA0KICAgIHNwcmludF9ncm91cCA9IGNhc2Vfd2hlbigNCiAgICAgIGNvdW50XzkwcGN0ID09IDAgICAgICAgICAgfiAiTG93IiwNCiAgICAgIGNvdW50XzkwcGN0ID49IDEgJiBjb3VudF85MHBjdCA8PSA3ICB+ICJNb2RlcmF0ZSIsDQogICAgICBjb3VudF85MHBjdCA+PSA4ICAgICAgICAgIH4gIkhpZ2giLA0KICAgICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICB+IE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkNCg0KIyBjaGVjayBjb3VudHMNCnRhYmxlKHdlZWtseV85MHBjdF9pbmp1cmllcyRzcHJpbnRfZ3JvdXAsIHVzZU5BID0gImlmYW55IikNCmBgYA0KDQpgYGB7cn0NCmdyb3VwX21vZGVsIDwtIGdsbSgNCiAgaW5qdXJ5IH4gc3ByaW50X2dyb3VwLA0KICBkYXRhID0gd2Vla2x5XzkwcGN0X2luanVyaWVzLA0KICBmYW1pbHkgPSAiYmlub21pYWwiDQopDQoNCnN1bW1hcnkoZ3JvdXBfbW9kZWwpDQpgYGANCg0KYGBge3J9DQp3ZWVrbHlfOTBwY3RfaW5qdXJpZXMgJT4lDQogIGdyb3VwX2J5KHNwcmludF9ncm91cCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBpbmp1cnlfcmF0ZSA9IG1lYW4oaW5qdXJ5LCBuYS5ybSA9IFRSVUUpLA0KICAgIG4gPSBuKCkNCiAgKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gc3ByaW50X2dyb3VwLCB5ID0gaW5qdXJ5X3JhdGUsIGZpbGwgPSBzcHJpbnRfZ3JvdXApKSArDQogIGdlb21fY29sKCkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KGluanVyeV9yYXRlLCBhY2N1cmFjeSA9IDAuMSkpLCB2anVzdCA9IC0wLjUpICsNCiAgbGFicyh0aXRsZSA9ICJJbmp1cnkgUmF0ZSBieSBTcHJpbnQgR3JvdXAiLA0KICAgICAgIHggPSAiU3ByaW50IEdyb3VwIiwNCiAgICAgICB5ID0gIkluanVyeSBSYXRlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCmBgYHtyIHJlbW92aW5nIHVubmVjZXNzYXJ5IG9iamVjdHMgZm9yIHJ1bm5pbmcgaW1iYWxhbmNlIGFuYWx5c2lzfQ0KI3JlbW92aW5nIGV4dHJhIGRhdGENCnJlbW92ZShiYW5kX2xvbmcsIGJpZ3MsIGNsZWFuX2Fub25zLCBjb21ibywgY29yX2RhdGEsIGNvcl9tYXRyaXgsIGRhaWx5XzkwX2NvdW50cywNCiAgICAgICBmdWxsX2dyaWQsIGhpdF85MF9jb3VudHMsIElEXzExLCBJbmNpZGVudF9kYXRlcywgaW5qdXJlZF9kYXRhLCBpbmp1cmllc19hbmRfcnVubmluZywNCiAgICAgICBpbmp1cnlfYnlfZ3JvdXAsIGluanVyeV93ZWVrcywgbWF4X3ZlbG9jaXRpZXMsIG1vZGVsX2FsbF9iYW5kcywgbW9kZWxfYmlnLA0KICAgICAgIG1vZGVsX2JpZ3MsIG1vZGVsX2NvbWJvLCBtb2RlbF9za2lsbCwgcGxheWVyX2NvdW50c19sb25nLCBwbGF5ZXJfZGF0YSwNCiAgICAgICBwbGF5ZXJfcG9zaXRpb25zLCBwbG90X2RhdGEsIHBsb3RzX2J5X3Bvc2l0aW9uLCBwb3NpdGlvbl9hdmVyYWdlcywNCiAgICAgICBwb3NpdGlvbl9hdmVyYWdlc193aXRoX3RlYW0sIHByZV9pbmp1cnlfd2Vla3MsIFFCcywgcmVsYXRpdmVfYmFuZHMsDQogICAgICAgcmVsYXRpdmVfYmFuZHNfbG9uZywgc2tpbGwsIHNwcmludF9jb3VudHMsIHNwcmludF9leHBvc3VyZV9iaWcsDQogICAgICAgc3ByaW50X2V4cG9zdXJlX2NvbWJvLCBzcHJpbnRfZXhwb3N1cmVfc2tpbGwsIHNwcmludF9pbmp1cnlfdGFibGUsIFY4LCANCiAgICAgICB3ZWVrbHlfOTBfY291bnRzLCB3ZWVrbHlfYmFuZF9lZmZvcnRfYnlfZ3JvdXAsIHdlZWtseV9lZmZvcnQsIHdlZWtseV9lZmZvcnRfbG9uZywNCiAgICAgICB3ZWVrbHlfZWZmb3J0X3dpZGUsIHdlZWtseV9tYXhfcGN0LCB3ZWVrbHlfbWF4X3ZlbG9jaXR5LCB3ZWVrbHlfc3ByaW50X2NvdW50cywNCiAgICAgICB3ZWVrbHlfc3ByaW50X2NvdW50c19sYWdnZWQsIHdlZWtseV9zcHJpbnRfZXhwb3N1cmUsIHdlZWtseV92ZWxvY2l0eV9lZmZvcnRzKQ0KDQojcmVtb3ZpbmcgZXh0cmEgdmFsdWVzIGFuZCBmdW5jdGlvbnMNCnJlbW92ZShhbGxfYXRobGV0ZXMsIGFsbF93ZWVrcywgYmFuZF9sZXZlbHMsIEJJRywgQ09NQk8sIFNLSUxMLCBpbmp1cmVkX2lkcywNCiAgICAgICBpbmp1cnlfd2Vla19udW1zLCBvdmVyYWxsX2F2ZywgcGxheWVyX2lkLCBwb3NpdGlvbnMsIFBvc2l0aW9ucywgcWJfYXZnLCB0ZWFtX2F2ZywNCiAgICAgICBwbG90X2hpdF85MF9ieV9wb3NpdGlvbikNCmBgYA0KDQoNCiMjIE1heGltdW0gVmVsb2NpdHkgVHJlbmRzIG92ZXIgVGltZQ0KYGBge3J9DQpjYXRhcHVsdF9pZHMgPC0gdW5pcXVlKENhdGFwdWx0X1Nlc3Npb25fY2xlYW4kYW5vbl9pZCkNCg0KQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKHdlZWsgPSBmbG9vcl9kYXRlKERhdGUsIHVuaXQ9IndlZWsiKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgd2VlaykgJT4lDQogIG11dGF0ZShXZWVrLk1heC5WZWxvY2l0eSA9IG1lYW4obmEub21pdChTZXNzaW9uLk1heC5WZWxvY2l0eSkpKQ0KDQprZW5kYWxscyA8LSByZXAoTkEsIDEwNCkNCm5lZ2F0aXZlX3RyZW5kIDwtICIwIg0KDQpmb3IoaSBpbiAxOjEwNCl7DQogIA0KICBzcGVlZHMgPC0gdW5pcXVlKENhdGFwdWx0X1Nlc3Npb25fY2xlYW5bQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiRhbm9uX2lkPT1jYXRhcHVsdF9pZHNbaV0sXSRXZWVrLk1heC5WZWxvY2l0eSkNCiAgd2Vla3MgPC0gcmV2KDE6bGVuZ3RoKHNwZWVkcykpDQogIA0KICBrZW5kIDwtIGNvcih3ZWVrcywgc3BlZWRzLCBtZXRob2Q9ImtlbmRhbGwiKQ0KICANCiAgaWYobGVuZ3RoKHNwZWVkcyk+NSl7DQogIGtlbmRhbGxzW2ldID0ga2VuZA0KICANCiAgICBpZihrZW5kIDw9IC0wLjUpew0KICAgICAgbmVnYXRpdmVfdHJlbmQgPC0gYyhuZWdhdGl2ZV90cmVuZCwgY2F0YXB1bHRfaWRzW2ldKQ0KICAgIH0NCiAgDQogIHAgPC0gZ2dwbG90KGZpbHRlcihDYXRhcHVsdF9TZXNzaW9uX2NsZWFuLCBhbm9uX2lkID09IGNhdGFwdWx0X2lkc1tpXSksIGFlcyh3ZWVrLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2Vlay5NYXguVmVsb2NpdHkpKSArDQogIGdlb21fcG9pbnQoY29sb3I9aWZlbHNlKGtlbmQgPD0gLTAuNSwgInJlZCIsICJibGFjayIpKSArDQogIGdlb21fbGluZShjb2xvcj1pZmVsc2Uoa2VuZCA8PSAtMC41LCAicmVkIiwgImJsYWNrIikpICsNCiAgbGFicyh0aXRsZSA9ICJBdmVyYWdlIFRvcCBSdW5uaW5nIFNwZWVkIHBlciBXZWVrIiwgc3VidGl0bGU9a2VuZCkgKw0KICB5bGltKDAsMjUpDQogICAgICAgDQogICAgICAgDQogIHByaW50KHApDQogIA0KICANCiAgfQ0KICANCn0NCmBgYA0KYGBge3J9DQpoaXN0KGtlbmRhbGxzKQ0KYWJsaW5lKHY9LTAuNSkNCmFibGluZSh2PTAuNSkgDQphYmxpbmUodj1tZWRpYW4obmEub21pdChrZW5kYWxscykpKQ0KYGBgDQpCYXNlZCBvbiB0aGUgS2VuZGFsbCBDb3JyZWxhdGlvbnMgY2FsY3VsYXRlZCBmcm9tIHRoZSBhdmVyYWdlIG1heGltdW0gdmVsb2NpdHkgcGVyIHdlZWsgZm9yIGVhY2ggcGxheWVyIHdlIGNhbiBzZWUgdGhhdCB0aGUgZGlzdHJpYnV0aW9ucyBvZiBjb3JyZWxhdGlvbnMgc2hvd3MgdGhhdCBtb3N0IHBsYXllcnMgdGVuZCB0byBoYXZlIGEgd2VhayBLZW5kYWxsIENvcnJlbGF0aW9uIGNvZWZmaWNpZW50LiBUaGlzIHN1Z2dlc3RzIHRoYXQgcGxheWVyIG1heSB0ZW5kIHRvIHNlZSBhIGRlY3JlYXNlIGluIG1heGltdW0gdmVsb2NpdHkgcGVyIHdlZWsgdGhyb3VnaG91dCB0aGVpciB0aW1lIGF0IENVLiBGb3IgbW9zdCBwbGF5ZXJzIHdpdGggYSBuZWdhdGl2ZSBjb2VmZmljaWVudCB0aG91Z2gsIGl0IGlzIGNvbnNpZGVyZWQgcmVhbGx5IHdlYWsuIFRoZSBwbGF5ZXJzIHdpdGggYSBzdHJvbmcgbmVnYXRpdmUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYXJlIElEIDMwLCBJRCAxNzgsIElEIDIyMCwgYW5kIElEIDY1LiBUaGVzZSBhcmUgdGhlIG9uZXMgdGhhdCBoYXZlIGFsbW9zdCBhIGRlZmluaXRpdmUgbmVnYXRpdmUgdHJlbmQgaW4gdGhlaXIgbWF4aW11bSBydW5uaW5nIHNwZWVkcy4gVGhlc2UgcGxheWVycyBhcmUgdGhlIG9ubHkgb25lcyB0aGF0IHdlIGNhbiBzYXkgZm9yIGNlcnRhaW4gaGF2ZSBzZWVuIGEgZGVjcmVhc2UgaW4gdGhlaXIgdG9wIHJ1bm5pbmcgc3BlZWQuIA0KDQpgYGB7cn0NCmRhdGFfMTMwIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIGZpbHRlcihhbm9uX2lkID09ICJJRF8xMzAiKQ0KZGF0YV8xNzggPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgPT0gIklEXzE3OCIpDQpkYXRhXzIyMCA8LSBDYXRhcHVsdF9TZXNzaW9uX2NsZWFuICU+JQ0KICBmaWx0ZXIoYW5vbl9pZCA9PSAiSURfMjIwIikNCmRhdGFfNjUgPC0gQ2F0YXB1bHRfU2Vzc2lvbl9jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgPT0gIklEXzY1IikNCg0KbW9kZWxfMTMwIDwtIGxtKFdlZWsuTWF4LlZlbG9jaXR5IH4gd2VlaywgZGF0YT1kYXRhXzEzMCkNCnN1bW1hcnkobW9kZWxfMTMwKQ0KbW9kZWxfMTc4IDwtIGxtKFdlZWsuTWF4LlZlbG9jaXR5IH4gd2VlaywgZGF0YT1kYXRhXzE3OCkNCnN1bW1hcnkobW9kZWxfMTc4KQ0KbW9kZWxfMjIwIDwtIGxtKFdlZWsuTWF4LlZlbG9jaXR5IH4gd2VlaywgZGF0YT1kYXRhXzIyMCkNCnN1bW1hcnkobW9kZWxfMjIwKQ0KbW9kZWxfNjUgPC0gbG0oV2Vlay5NYXguVmVsb2NpdHkgfiB3ZWVrLCBkYXRhPWRhdGFfNjUpDQpzdW1tYXJ5KG1vZGVsXzY1KQ0KYGBgDQpMb29raW5nIGF0IHRoZSBzbG9wZSBjb2VmZmljaWVudHMgZm9yIHBsYXllcnMgdGhhdCBoYWQgdGhlIGxvd2VzdCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgd2UgY2FuIHNlZSB0aGF0IGFsbCBvZiB0aGVtIGhhZCBhIHJlbGF0aXZlbHkgc21hbGwgc2xvcGUgdmFsdWUuIEFsbCBvZiB0aGVtIGJlaW5nIGxvd2VyIHRoYW4gMC4wNCBpbiBhYnNvbHV0ZSB2YWx1ZS4gVGhpcyBzdWdnZXN0cyB0aGF0IHdoaWxlIHRoZXkgaGFkIGEgZGV0ZWN0YWJsZSBuZWdhdGl2ZSB0cmVuZCB0aHJvdWdob3V0IHRoZWlyIHRpbWUgYXQgQ1UsIG5vbmUgb2YgdGhlbSBoYWQgYW55IHNoYXJwIGRlY3JlYXNlcy4gDQoNCg0KIyBTZWN0aW9uIDI6IFJ1bm5pbmcgSW1iYWxhbmNlDQoNCiMjIFdoYXQgaXMgdGhlIHZhcmlhdGlvbiBhdCB0aGUgdGVhbSBsZXZlbCBhbmQgYXQgZWFjaCBpbmRpdmlkdWFsIGF0aGxldGUgbGV2ZWw/DQoNCiAgTG9va2luZyBhdCB0ZWFtIGRhdGEgYXMgYSB3aG9sZSwgc2luY2UgSmFudWFyeSAxLCAyMDI0IHRoZXJlIGlzIGFic29sdXRlIG5vIGRldmlhbmNlIGZyb20gMC4gVGhhdCBtZWFucyB0aGF0IHNpbmNlIEphbnVhcnkgMSwgMjAyNCwgdGhlIHRlYW0gaGFzIGhhZCB0aGUgc2FtZSBhdmVyYWdlIHJ1bm5pbmcgaW1iYWxhbmNlIG9mIDAuIFRoaXMgbWFrZXMgc2Vuc2UgZ2l2ZW4gdGhhdCB0aGUgdGVhbSBpcyBzbyBsYXJnZSBhbmQgdGhhdCBpbWJhbGFuY2VzIGNhbiBnbyBmcm9tIC0xMDAlIHRvIDEwMCUuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aHJvdWdob3V0IHRoaXMgdGltZSwgYXQgbm8gcG9pbnQgd2FzIHRoZXJlIGEgdGVhbSBzd2F5IHRvIG9uZSBzaWRlLiBUaGVyZSBhbHNvIHdlcmVuJ3QgYW55IHBvaW50cyBzaW5jZSBKYW51YXJ5IDEsIDIwMjQgdGhhdCB0aGUgdGVhbSBoYWQgYW55IGxhcmdlIHNwaWtlcyBpbiBhdmVyYWdlIGFic29sdXRlIHZhbHVlIG9mIHJ1bm5pbmcgaW1iYWxhbmNlLiBUaGlzIHN1Z2dlc3RzIHRoYXQgYXQgbm8gcG9pbnQgdGhyb3VnaG91dCB0aGUgc2Vhc29uIHdlcmUgdGhlcmUgbGFyZ2VyIHNwaWtlcyB0aGFuIG5vcm1hbCBpbiBydW5uaW5nIGltYmFsYW5jZS4gDQogIEVhY2ggcGxheWVyIHRlbmRzIHRvIGhhdmUgYSB2ZXJ5IHVuaXF1ZSB0cmVuZCBpbiB0aGVpciBydW5uaW5nIGltYmFsYW5jZS4gTG9va2luZyBhdCBob3cgdGhlIHRlYW0gdmFyaWVzIGJ1dCBhbHNvIGF0IGhvdyBlYWNoIHBsYXllciB2YXJpZXMgdGhyb3VnaG91dCB0aGUgc2Vhc29uLCBpdCdzIGhhcmQgdG8gbWFrZSBvdXQgYW55IHBhdHRlcm4gdGhhdCdzIGFwcGxpY2FibGUgdG8gbW9zdCBwZW9wbGUuIFRoZSB2YXJpYW5jZSBvZiBydW5uaW5nIGltYmFsYW5jZSB2YXJpZXMgZ3JlYXRseSBiZXR3ZWVuIGVhY2ggcGxheWVyLiBJbnN0ZWFkIHdlIGxvb2tlZCBhdCB0aGUgdmFyaWFuY2VzIGJldHdlZW4gcGxheWVycyB3aG8gd2VyZSBpbmp1cmVkIGFuZCB0aG9zZSB3aG8gd2VyZSBub3QuIEJhc2VkIG9uIHRocmVlIGRpZmZlcmVudCBib290c3RyYXBwZWQgZmluZGluZ3MsIHdlIGNhbiBzZWUgdGhhdCB0aGUgdmFyaWFuY2VzIGJldHdlZW4gcGxheWVycyB3aG8gd2VyZSBpbmp1cmVkIGFuZCB0aG9zZSB3aG8gd2VyZSBub3Qgd2VyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50Lg0KICANCiAgRm9yIHRoZSBmaXJzdCBib290c3RyYXAsIHdlIGNvbXBhcmVkIHRoZSB2YXJpYW5jZSBvZiB0aGUgcG9vbGVkIGdyb3VwcyBtZWFuaW5nIHRoYXQgdGhlIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGZvciBwbGF5ZXJzIHdobyB3ZXJlIGluanVyZWQgYW5kIHRob3NlIHdobyB3ZXJlIG5vdCB3ZXJlIGNvbXBhcmVkLiBUaGlzIHJlc3VsdGVkIGluIGEgOTAlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgd2hpY2ggc3VnZ2VzdGVkIHRoYXQgdGhlIGRpZmZlcmVuY2UgaW4gdmFyaWFuY2UgYmV0d2VlbiB0aGUgdHdvIGdyb3VwcyBpcyBiZXR3ZWVuIDAuMzAgYW5kIDMuNDUuIFRoaXMgc3VnZ2VzdHMgdGhhdCB3aGVuIGxvb2tpbmcgYXQgdGhlIHZhcmlhbmNlIG9mIHRoZSB0d28gZ3JvdXBzIHNlcGFyYXRlbHkgYnV0IGFsbCB0aGUgcGxheWVycyBhcmUgcG9vbGVkIHRvZ2V0aGVyLCB0aGUgdmFyaWFuY2VzIHdpbGwgbW9zdCBsaWtlbHkgYmUgZGlmZmVyZW50IGJ5IGZhY3RvciBiZXR3ZWVuIDAuMzAgYW5kIDMuNDUgYW5kIHRoZSB2YXJpYW5jZSBmb3IgdGhlIGluanVyZWQgcG9vbCB3aWxsIGJlIGdyZWF0ZXIgdGhhbiB0aGF0IG9mIHRoZSB1bmluanVyZWQgcG9vbC4NCiAgRm9yIHRoZSBzZWNvbmQgYm9vdHN0cmFwLCBlYWNoIHBsYXllcidzIHZhcmlhbmNlIHdhcyB0YWtlbiBpbmRpdmlkdWFsbHkuIFRoaXMgdW5wb29sZWQgYXBwcm9hY2ggd2FzIHRha2VuIHRvIHNlZSBpZiBhbiBpbmRpdmlkdWFsIHBsYXllcidzIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGNvdWxkIHBvdGVudGlhbGx5IGJlIHJlbGF0ZWQgdG8gSFNJIHJpc2suIFRoZSBib290c3RyYXAgYWxnb3JpdGhtIGluIHRoaXMgY2FzZSB0b29rIHRoZSBhdmVyYWdlZCB2YXJpYW5jZXMgb2YgdGhlIGJvb3RzdHJhcHBlZCBzYW1wbGUgZm9yIGVhY2ggZ3JvdXAgYW5kIGNvbXBhcmVkIHRoZW0uIFRoaXMgYm9vdHN0cmFwIHByb2R1Y2VkIGEgOTAlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSBkaWZmZXJlbmNlIGluIGF2ZXJhZ2UgdmFyaWFuY2UgYmV0d2VlbiB0aGUgdHdvIGdyb3VwcyBpcyAwLjc5IHRvIDEuMzIuIFRoZXNlIHJlc3VsdHMgc3VnZ2VzdCB0aGF0IHBsYXllcnMgd2hvIHN1c3RhaW5lZCBhIGhhbXN0cmluZyBpbmp1cnkgc2luY2UgSmFudWFyeSAxLCAyMDI0IGhhZCwgb24gYXZlcmFnZSwgYSBncmVhdGVyIHZhcmlhbmNlIGluIHRoZWlyIHJ1bm5pbmcgaW1iYWxhbmNlIGJ5IGFib3V0IDEuMDYuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGFuZCBIU0kgcmlzay4gVGhpcyBmb3VuZCBpbmNyZWFzZSBpbiB2YXJpYWJpbGl0eSB3aWxsIGJlIHVzZWQgdG8gYWRkcmVzcyB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ucy4NCiAgRm9yIHRoZSB0aGlyZCBib290c3RyYXAsIHdlIHdhbnRlZCB0byBzZWUgaWYgdGhlcmUgd2FzIGEgZGlmZmVyZW5jZSBpbiB0aGUgYXZlcmFnZSBtZWFuIGFic29sdXRlIHZhbHVlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGJldHdlZW4gcGxheWVycyB3aG8gd2VyZSBhbmQgd2VyZW4ndCBpbmp1cmVkLiBUaGUgYm9vdHN0cmFwcGluZyBhbGdvcml0aG0gZm9yIHRoaXMgdGVzdCBjYWxjdWxhdGVkIHRoZSBhdmVyYWdlIGFic29sdXRlIGRpc3RhbmNlIHZhbHVlIG9yIGVhY2ggcnVubmluZyBpbWJhbGFuY2UgbWVhc3VyZW1lbnQgYW5kIGZvdW5kIHRoZSBhdmVyYWdlIGZvciBlYWNoIHNhbXBsZS4gVGhpcyB0ZXN0IGZvdW5kIHRoYXQgYXQgdGhlIDkwJSBzaWduaWZpY2FuY2UgbGV2ZWwsIGluanVyZWQgcGxheWVycyBoYWQgYW4gYXZlcmFnZSBydW5uaW5nIGltYmFsYW5jZSBhYnNvbHV0ZSB2YWx1ZSBiZXR3ZWVuIDAuMDYgYW5kIDAuMzIgZ3JlYXRlciB0aGFuIHRoZWlyIHVuaW5qdXJlZCBjb3VudGVycGFydHMuIFRoZXNlIHZhbHVlcyB0aG91Z2gsIHdoZW4gd2UgY29uc2lkZXIgdGhhdCB0aGUgcmFuZ2Ugb2YgcnVubmluZyBpbWJhbGFuY2UgZ29lcyBmcm9tIDAgdG8gMTAwIGlzIHNtYWxsIGFuZCBtYXkgYmUgaGFyZCB0byBkZXRlY3Qgd2hlbiBvdXQgaW4gdGhlIGZpZWxkLiANCiAgDQogIFdlIGFsc28gbG9va2VkIGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBydW5uaW5nIGltYmFsYW5jZSBhbmQgaGlnaGVyIGxldmVsIHBvc2l0aW9uLiBGcm9tIHRoaXMgYW5hbHlzaXMgd2UgZm91bmQgdGhhdCB0aGVyZSBkb2Vzbid0IHNlZW0gdG8gYmUgYSBzdXBlciBzdHJvbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHRocmVlIGNhdGVnb3JpZXMgYW5kIGF2ZXJhZ2UgcnVubmluZyBpbWJhbGFuY2UgdmFyaWFuY2UuIFRoZSBib290c3RyYXAgcmV2ZWFsZWQgdGhhdCB0aGVyZSBhcmUgcG90ZW50aWFsbHkgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aG9zZSB3aG8gYXJlIEJpZ3MgYW5kIENvbWJvcy4gQnV0LCB0aG9zZSB3aG8gYXJlIFNraWxscyB3ZXJlbid0IGFibGUgdG8gZGlmZmVyZW50aWF0ZSB0aGVtc2VsdmVzIGJldHdlZW4gdGhlIHR3byBncm91cHMuIA0KICBBbG9uZyB3aXRoIHRoaXMsIHdlIGxvb2tlZCBhdCB0aGUgYXZlcmFnZSBhYnNvbHV0ZSB2YWx1ZSBpbiBydW5uaW5nIGltYmFsYW5jZSBmb3IgdGhlIHRocmVlIGdyb3Vwcy4gVGhpcyBhbmFseXNpcyB0b2xkIHVzIHRoYXQgd2hpbGUgQ29tYm9zIGFuZCBCaWdzIHRlbmRlZCB0byBoYXZlIHRoZSBzYW1lIGF2ZXJhZ2UgYWJzb2x1dGUgdmFsdWUgcnVubmluZyBpbWJhbGFuY2UsIFNraWxscyBoYWQgYSBzaWduaWZpY2FudGx5IGhpZ2hlciBhdmVyYWdlIGFic29sdXRlIHJ1bm5pbmcgaW1iYWxhbmNlIHZhbHVlLiANCiAgV2UgY2FuIHNlZSBmcm9tIHRoZSB2ZXJ5IGxhc3QgY2hhcnQgaW4gdGhpcyBhbmFseXNpcyB0aGF0IFNraWxscyBtYWtlIHVwIHRoZSBtb3N0IG9mIHRob3NlIHdpdGggaGFtc3RyaW5nIGluanVyaWVzIGZvbGxvd2VkIGNsb3NlbHkgYnkgQ29tYm9zIGFuZCBCaWdzIG1ha2luZyB1cCBhcm91bmQgaGFsZiBvZiB0aGUgYW1vdW50IG9mIFNraWxscy4gVGhpcyBpcyBpbnRlcmVzdGluZyBjb25zaWRlcmluZyB0aGF0IHRoZSBhbW91bnRzIG9mIEJpZ3MsIFNraWxscywgYW5kIENvbWJvcyB3aXRoaW4gdGhlIEhpc3RvcmljYWwgUnVubmluZyBkYXRhIHNldCBhcmUgYWxsIHJvdWdobHkgdGhlIHNhbWUuIA0KDQojIyMgVGVhbSBBbmFseXNpcw0KYGBge3IgTG9va2luZyBhdCB0ZWFtIGFuZCBwbGF5ZXIgdmFyaWFuY2VzIG9mIHJ1bm5pbmcgaW1iYWxhbmNlfQ0KI3RlYW0gdmFyaWF0aW9uDQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIHN1bW1hcml6ZShUZWFtX1ZhcmlhdGlvbiA9IHZhcihSdW5uaW5nLkltYmFsYW5jZSkpDQoNCiNpbmRpdmlkdWFsIHBsYXllciB2YXJpYXRpb24NCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIHN1bW1hcml6ZShQbGF5ZXJfVmFyaWF0aW9uID0gdmFyKG5hLm9taXQoUnVubmluZy5JbWJhbGFuY2UpKSkgJT4lDQogIHVuZ3JvdXAoKSANCg0KI2F2ZXJhZ2UgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgYWNyb3NzIGFsbCBwbGF5ZXJzDQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGdyb3VwX2J5KGFub25faWQpICU+JQ0KICBtdXRhdGUoUGxheWVyLnZhciA9IHZhcihuYS5vbWl0KFJ1bm5pbmcuSW1iYWxhbmNlKSkpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIHN1bW1hcml6ZShBdmVyYWdlX1BsYXllcl9WYXJpYW5jZSA9IG1lYW4obmEub21pdChQbGF5ZXIudmFyKSkpDQoNCiNtYWtpbmcgdmFyaWFuY2UgYW5kIGF2ZXJhZ2UgYWJzb2x1dGUgdmFsdWUgZm9yIGVhY2ggZGF0ZSB0byBzZWUgdHJlbmRzDQpIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBncm91cF9ieShEYXRlKSAlPiUNCiAgbXV0YXRlKERhdGUuVmFyaWFuY2UgPSB2YXIobmEub21pdChSdW5uaW5nLkltYmFsYW5jZSkpLA0KICAgICAgICAgRGF0ZS5BdmcuQWJzLlZhbHVlID0gbWVhbihhYnMobmEub21pdChSdW5uaW5nLkltYmFsYW5jZSkpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCg0KYGBge3IgUnVubmluZyBpbWJhbGFuY2UgbWVhc3VyZW1lbnRzIGZvciB3aG9sZSB0ZWFtIHNpbmNlIEphbnVhcnkgMSwgMjAyNH0NCiNjYWxjdWxhdGluZyBtZWFuIGFuZCB2YXJpYW5jZSBmb3IgdGVhbSBkYXRhDQp0ZWFtX21lYW4gPC0gbWVhbihIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kUnVubmluZy5JbWJhbGFuY2UpDQp0ZWFtX3NkIDwtIHNkKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiRSdW5uaW5nLkltYmFsYW5jZSkNCg0KI21ha2luZyBzY2F0dGVyIHBsb3Qgb2YgdGVhbSBydW5uaW5nIGltYmFsYW5jZSBkYXRhIHRocm91Z2hvdXQgc2Vhc29uDQpnZ3Bsb3QoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9tZWFuLCBjb2xvciA9ICIjQ0ZCODdDIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0ZWFtX21lYW4gKyB0ZWFtX3NkKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fbWVhbiAtIHRlYW1fc2QpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gdGVhbV9tZWFuICsgKDIqdGVhbV9zZCksIGNvbG9yID0gIiNBMkE0QTMiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHRlYW1fbWVhbiAtICgyKnRlYW1fc2QpLCBjb2xvciA9ICIjQTJBNEEzIikgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIGxhYnModGl0bGUgPSAiVGVhbSBSdW5uaW5nIEltYmFsYW5jZSBTaW5jZSBKYW51YXJ5IDEsIDIwMjQiLCB5PSJSdW5uaW5nIEltYmFsYW5jZSAoJSkiLA0KICAgICAgIHN1YnRpdGxlID0gIlx1MDNCQyA9IDAuMDg2MjM0MTIsIFx1MDNDM14yID0gMTQuOTQyMTUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojbWFraW5nIHNjYXR0ZXIgcGxvdCBvZiB0ZWFtIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEgdGhyb3VnaG91dCBzZWFzb24NCmdncGxvdChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMykgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0ZWFtX21lYW4sIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV9saW5lKGFlcyh4PURhdGUsIHk9RGF0ZS5BdmcuQWJzLlZhbHVlKSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV9saW5lKGFlcyh4PURhdGUsIHk9LURhdGUuQXZnLkFicy5WYWx1ZSksIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIGxhYnModGl0bGUgPSAiVGVhbSBSdW5uaW5nIEltYmFsYW5jZSBTaW5jZSBKYW51YXJ5IDEsIDIwMjQiLCB5PSJSdW5uaW5nIEltYmFsYW5jZSAoJSkiLA0KICAgICAgIHN1YnRpdGxlID0gIlx1MDNCQyA9IDAuMDg2MjM0MTIsIFx1MDNDM14yID0gMTQuOTQyMTUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojbWFraW5nIGhpc3RvZ3JhbSBvZiB0ZWFtIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGENCmdncGxvdChIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4sIGFlcyhSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGxhYnModGl0bGUgPSAiVGVhbSBSdW5uaW5nIEltYmFsYW5jZSBTaW5jZSBKYW51YXJ5IDEsIDIwMjQiLCB4PSJSdW5uaW5nIEltYmFsYW5jZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQojIyMgSW5qdXJlZCBBbmFseXNpcw0KYGBge3IgTG9va2luZyBhdCBkaXN0cmlidXRpb25zIGZvciBpbmp1cmVkIGFuZCB1bmluanVyZWQgc2VwYXJhdGVseX0NCiNtYWtpbmcgaGlzdG9ncmFtIGZvciBydW5uaW5nIGltYmFsYW5jZSBvZiBhbGwgaW5qdXJlZCBhdGhsZXRlcw0KZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMsXSwgYWVzKFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiNDRkI4N0MiLCBhbHBoYSA9IDAuNzUpICsNCiAgI2FkZGluZyBpbiA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbA0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjAyNSksIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuOTc1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgeGxpbSgtMjEsMjEpICsNCiAgbGFicyh0aXRsZSA9ICJSdW5uaW5nIEltYmFsYW5jZSBmb3IgUGxheWVycyB3aXRoIEhTSSBzaW5jZSBKYW51YXJ5IDEsIDIwMjQiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojbWFraW5nIGhpc3RvZ3JhbSBmb3IgcnVubmluZyBpbWJhbGFuY2Ugb2YgYWxsIHVuaW5qdXJlZCBhdGhsZXRlcw0KZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdLCBhZXMoUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiYmxhY2siLCBhbHBoYSA9IDAuNzUpICsNCiAgI2FkZGluZyBpbiA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbA0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuMDI1KSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuOTc1KSkgKw0KICB4bGltKC0yMSwyMSkgKw0KICBsYWJzKHRpdGxlID0gIlJ1bm5pbmcgSW1iYWxhbmNlIGZvciBQbGF5ZXJzIHdpdGhvdXQgSFNJIHNpbmNlIEphbnVhcnkgMSwgMjAyNCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiNQbG90dGluZyBpbmp1cmVkIGFuZCB1bmluanVyZWQgaGlzdG9ncmFtcyBvdmVyIHRvcCBvbmUgYW5vdGhlcg0KZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdLCBhZXMoUnVubmluZy5JbWJhbGFuY2UpKSArDQogIGdlb21faGlzdG9ncmFtKGFscGhhID0gMC43NSkgKw0KICAjYWRkaW5nIGluIDk1JSBDSSBmb3IgdW5pbmp1cmVkIHBsYXllcnMNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjA1KSkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSB1bmluanVyZWRfSURzLF0kUnVubmluZy5JbWJhbGFuY2UsIDAuOTUpKSArDQogIGdlb21faGlzdG9ncmFtKGRhdGEgPSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdLCBhZXMoUnVubmluZy5JbWJhbGFuY2UpLCBmaWxsID0gIiNDRkI4N0MiLCBhbHBoYSA9IDAuNzUpICsNCiAgI2FkZGluZyBpbiA5NSUgQ0kgZm9yIGluanVyZWQgcGxheWVycw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgJWluJSBpbmp1cmVkX0lEcyxdJFJ1bm5pbmcuSW1iYWxhbmNlLCAwLjA1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMsXSRSdW5uaW5nLkltYmFsYW5jZSwgMC45NSksIGNvbG9yID0gIiNDRkI4N0MiKSArDQogIHhsaW0oLTIxLDIxKSArDQogIGxhYnModGl0bGUgPSAiUnVubmluZyBJbWJhbGFuY2UgZm9yIFBsYXllcnMgd2l0aCBhbmQgd2l0aG91dCBIU0kgc2luY2UgSmFudWFyeSAxLCAyMDI0IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCmBgYHtyIGxvb2tpbmcgYXQgdHJlbmRzIGZvciBpbmp1cmVkIGFuZCB1bmluanVyZWQgcGxheWVycyBzaW5jZSAxLTEtMjAyNH0NCiNtYWtpbmcgc2NhdHRlciBwbG90IG9mIHJ1bm5pbmcgaW1iYWxhbmNlIGRhdGEgZm9yIGluanVyZWQgcGxheWVycw0KZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMsXSwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMykgKw0KICB5bGltKC0yMSwyMSkgKw0KICBsYWJzKHRpdGxlID0gIlJ1bm5pbmcgSW1iYWxhbmNlIGZvciBQbGF5ZXJzIHdpdGggSFNJIHNpbmNlIEphbnVhcnkgMSwgMjAyNCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCiNtYWtpbmcgc2NhdHRlciBwbG90IG9mIHJ1bm5pbmcgaW1iYWxhbmNlIGZvciB1bmluanVyZWQgcGxheWVycw0KZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArIA0KICBnZW9tX3BvaW50KGFscGhhID0gMC4zKSArDQogIHlsaW0oLTIxLDIxKSArDQogIGxhYnModGl0bGUgPSAiUnVubmluZyBJbWJhbGFuY2UgZm9yIFBsYXllcnMgd2l0aG91dCBIU0kgc2luY2UgSmFudWFyeSAxLCAyMDI0IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCiMjIyMgQm9vdHN0cmFwcGluZyBEaWZmZXJlbmNlcyBiZXR3ZWVuIEluanVyZWQgYW5kIFVuaW5qdXJlZCBBdGhsZXRlcw0KYGBge3IgU2VwYXJhdGluZyBpbmp1cmVkIGFuZCB1bmluanVyZWQgaW50byB0d28gZGlmZmVyZW50IGRhdGEgc2V0c30NCiNTcGxpdHRpbmcgdXAgdGhlIGRhdGEgc2V0cyBhbmQgY2FsY3VsYXRpbmcgcGxheWVyIHZhcmlhbmNlIGFuZCBtZWFzdXJlbWVudCBhYnNvbHV0ZSB2YWx1ZQ0KaW5qdXJlZF9kYXRhIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCAlaW4lIGluanVyZWRfSURzLF0gJT4lDQogIG11dGF0ZShQbGF5ZXIuQWJzb2x1dGUuRGlzdCA9IGFicyhSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICBncm91cF9ieShhbm9uX2lkKSAlPiUNCiAgbXV0YXRlKFBsYXllci5WYXJpYW5jZSA9IHZhcihSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICB1bmdyb3VwKCkNCg0KdW5pbmp1cmVkX2RhdGEgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkICVpbiUgdW5pbmp1cmVkX0lEcyxdICU+JQ0KICBtdXRhdGUoUGxheWVyLkFic29sdXRlLkRpc3QgPSBhYnMoUnVubmluZy5JbWJhbGFuY2UpKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShQbGF5ZXIuVmFyaWFuY2UgPSB2YXIoUnVubmluZy5JbWJhbGFuY2UpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KDQpgYGB7ciBCb29zdHJhcHBpbmcgdG8gbG9vayBhdCB2YXJpYW5jZXMgb2YgZGlmZmVyZW50IGdyb3VwcywgcG9vbGVkfQ0KI21ha2luZyBhIGRhdGEgZnJhbWUgdG8gaG9sZCBhbGwgb2YgdGhlIHdpdGhpbiBncm91cCB2YXJpYW5jZXMNCmdyb3VwX3ZhcmlhbmNlcyA8LSBkYXRhLmZyYW1lKGluanVyZWRfdmFyID0gcmVwKE5BLDUwMDApLA0KICAgICAgICAgICAgICAgIHVuaW5qdXJlZF92YXIgPSByZXAoTkEsNTAwMCksDQogICAgICAgICAgICAgICAgZGlmZl9pbl92YXIgPSByZXAoTkEsIDUwMDApKQ0KDQojYm9vdHN0cmFwIGZvciB2YXJpYW5jZSwgNTAwMCBpdGVyYXRpb25zDQpmb3IoaSBpbiAxOjUwMDApew0KICAjcmFuZG9tIHNlZWQNCiAgc2V0LnNlZWQoaSkgDQogIA0KICAjdGFraW5nIHNhbXBsZXMgZnJvbSBlYWNoIG9mIHRoZSBkYXRhIHNldHMsIHNhbWUgbnVtYmVyIG9mIHJvd3MsIHJlcGxhY2VtZW50IHRydWUNCiAgaW5qdXJlZF9zYW1wbGUgPC0gc2FtcGxlX24oaW5qdXJlZF9kYXRhLCByZXBsYWNlID0gVFJVRSwgc2l6ZSA9IDE2NzIpDQogIHVuaW5qdXJlZF9zYW1wbGUgPC0gc2FtcGxlX24odW5pbmp1cmVkX2RhdGEsIHJlcGxhY2U9VFJVRSwgc2l6ZSA9IDIzOTEpDQogIA0KICAjc3RvcmluZyB0aGUgY2FsY3VsYXRlZCB2YXJpYW5jZXMgaW4gZGF0YSBmcmFtZQ0KICBncm91cF92YXJpYW5jZXNbaSwxXSA9IHZhcihpbmp1cmVkX3NhbXBsZSRSdW5uaW5nLkltYmFsYW5jZSkNCiAgZ3JvdXBfdmFyaWFuY2VzW2ksMl0gPSB2YXIodW5pbmp1cmVkX3NhbXBsZSRSdW5uaW5nLkltYmFsYW5jZSkNCiAgZ3JvdXBfdmFyaWFuY2VzW2ksM10gPSBncm91cF92YXJpYW5jZXNbaSwxXSAtIGdyb3VwX3ZhcmlhbmNlc1tpLDJdDQp9DQpgYGANCg0KDQpgYGB7ciBQbG90dGluZyByZXN1bHRzIGZyb20gcG9vbGVkIGJvb3N0cmFwcGVkIHZhcmlhbmNlc30NCmdncGxvdChkYXRhPWdyb3VwX3ZhcmlhbmNlcywgYWVzKGRpZmZfaW5fdmFyKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfdmFyaWFuY2VzJGRpZmZfaW5fdmFyLCAwLjA1KSwgY29sb3I9ICIjQ0ZCODdDIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF92YXJpYW5jZXMkZGlmZl9pbl92YXIsIDAuOTUpLCBjb2xvcj0gIiNDRkI4N0MiKSArDQogIGxhYnMoeD0iRGlmZmVyZW5jZSBpbiBWYXJpYW5jZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdChkYXRhPWdyb3VwX3ZhcmlhbmNlcykgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoaW5qdXJlZF92YXIpLCBhbHBoYSA9IDAuNzUsIGZpbGwgPSIjQ0ZCODdDIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXModW5pbmp1cmVkX3ZhciksIGFscGhhID0gMC43NSkgKw0KICBsYWJzKHg9IlZhcmlhbmNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCmBgYHtyIEJvb3RzdHJhcHBpbmcgdG8gbG9vayBhdCBhdmVyYWdlIHBsYXllciB2YXJpYW5jZXN9DQojbWFraW5nIGEgZGF0YSBmcmFtZSB0byBob2xkIGFsbCBvZiB0aGUgYXZlcmFnZSBwbGF5ZXIgdmFyaWFuY2VzIGJldHdlZW4gZ3JvdXBzDQptZWFuX3BsYXllcl92YXJpYW5jZXMgPC0gZGF0YS5mcmFtZShpbmp1cmVkX3ZhciA9IHJlcChOQSw1MDAwKSwNCiAgICAgICAgICAgICAgICB1bmluanVyZWRfdmFyID0gcmVwKE5BLDUwMDApLA0KICAgICAgICAgICAgICAgIGRpZmZfaW5fdmFyID0gcmVwKE5BLCA1MDAwKSkNCg0KZm9yKGkgaW4gMTo1MDAwKXsNCiAgI3JhbmRvbSBzZWVkDQogIHNldC5zZWVkKGkpIA0KICANCiAgI3Rha2luZyBzYW1wbGVzIGZyb20gZWFjaCBvZiB0aGUgZGF0YSBzZXRzLCBzYW1lIG51bWJlciBvZiByb3dzLCByZXBsYWNlbWVudCB0cnVlDQogIGluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKGluanVyZWRfZGF0YSwgcmVwbGFjZSA9IFRSVUUsIHNpemUgPSAxNjcyKQ0KICB1bmluanVyZWRfc2FtcGxlIDwtIHNhbXBsZV9uKHVuaW5qdXJlZF9kYXRhLCByZXBsYWNlPVRSVUUsIHNpemUgPSAyMzkxKQ0KICANCiAgI3N0b3JpbmcgdGhlIGNhbGN1bGF0ZWQgdmFyaWFuY2VzIGluIGRhdGEgZnJhbWUNCiAgbWVhbl9wbGF5ZXJfdmFyaWFuY2VzW2ksMV0gPSBtZWFuKG5hLm9taXQoaW5qdXJlZF9zYW1wbGUkUGxheWVyLlZhcmlhbmNlKSkNCiAgbWVhbl9wbGF5ZXJfdmFyaWFuY2VzW2ksMl0gPSBtZWFuKG5hLm9taXQodW5pbmp1cmVkX3NhbXBsZSRQbGF5ZXIuVmFyaWFuY2UpKQ0KICBtZWFuX3BsYXllcl92YXJpYW5jZXNbaSwzXSA9IG1lYW5fcGxheWVyX3ZhcmlhbmNlc1tpLDFdIC0gbWVhbl9wbGF5ZXJfdmFyaWFuY2VzW2ksMl0NCn0NCmBgYA0KDQoNCmBgYHtyIFBsb3R0aW5nIHJlc3VsdHMgZnJvbSBhdmVyYWdlZCBib290c3RyYXBwZWQgcGxheWVyIHZhcmlhbmNlc30NCmdncGxvdChkYXRhID0gbWVhbl9wbGF5ZXJfdmFyaWFuY2VzLCBhZXMoZGlmZl9pbl92YXIpKSArDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShtZWFuX3BsYXllcl92YXJpYW5jZXMkZGlmZl9pbl92YXIsIDAuMDUpLCBjb2xvcj0gIiNDRkI4N0MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKG1lYW5fcGxheWVyX3ZhcmlhbmNlcyRkaWZmX2luX3ZhciwgMC45NSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgbGFicyh0aXRsZSA9ICJEaWZmZXJlbmNlIGluIEVzdGltYXRlZCBBdmVyYWdlIFZhcmlhbmNlIGZvciBJbmp1cmVkIGFuZCBVbmluanVyZWQgQXRobGV0ZXMiLCB4PSJEaWZmZXJlbmNlIGluIEF2ZXJhZ2UgVmFyaWFuY2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3QoZGF0YT1tZWFuX3BsYXllcl92YXJpYW5jZXMpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKGluanVyZWRfdmFyKSwgYWxwaGEgPSAwLjc1LCBmaWxsID0iI0NGQjg3QyIpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHVuaW5qdXJlZF92YXIpLCBhbHBoYSA9IDAuNzUpICsNCiAgbGFicyh0aXRsZSA9ICJFc3RpbWF0ZWQgQXZlcmFnZSBWYXJpYW5jZSBmb3IgSW5qdXJlZCBhbmQgVW5pbmp1cmVkIEF0aGxldGVzIiwgeD0iQXZlcmFnZSBWYXJpYW5jZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpgYGB7ciBCb290c3RyYXBwaW5nIHRvIGxvb2sgYXQgYXZlcmFnZSBhYnNvbHV0ZSB2YWx1ZSBydW5uaW5nIGltYmFsYW5jZX0NCiNtYWtpbmcgYSBkYXRhIGZyYW1lIHRvIGhvbGQgYWxsIG9mIHRoZSBhdmVyYWdlIGFic29sdXRlIGRpZmZlcmVuY2VzIGZyb20gMA0KZ3JvdXBfZGlzdGFuY2UgPC0gZGF0YS5mcmFtZShpbmp1cmVkX2Rpc3QgPSByZXAoTkEsNTAwMCksDQogICAgICAgICAgICAgICAgdW5pbmp1cmVkX2Rpc3QgPSByZXAoTkEsNTAwMCksDQogICAgICAgICAgICAgICAgZGlmZl9pbl9kaXN0ID0gcmVwKE5BLCA1MDAwKSkNCg0KI2Jvb3RzdHJhcCBmb3IgdmFyaWFuY2VzLCA1MDAwIGl0ZXJhdGlvbnMNCmZvcihpIGluIDE6NTAwMCl7DQogICNyYW5kb20gc2VlZA0KICBzZXQuc2VlZChpKSANCiAgDQogICN0YWtpbmcgc2FtcGxlcyBmcm9tIGVhY2ggb2YgdGhlIGRhdGEgc2V0cywgc2FtZSBudW1iZXIgb2Ygcm93cywgcmVwbGFjZW1lbnQgdHJ1ZQ0KICBpbmp1cmVkX3NhbXBsZSA8LSBzYW1wbGVfbihpbmp1cmVkX2RhdGEsIHJlcGxhY2UgPSBUUlVFLCBzaXplID0gMTY1OCkNCiAgdW5pbmp1cmVkX3NhbXBsZSA8LSBzYW1wbGVfbih1bmluanVyZWRfZGF0YSwgcmVwbGFjZT1UUlVFLCBzaXplID0gMjQwNSkNCiAgDQogICNzdG9yaW5nIHRoZSBjYWxjdWxhdGVkIHZhcmlhbmNlcyBpbiBkYXRhIGZyYW1lDQogIGdyb3VwX2Rpc3RhbmNlW2ksMV0gPSBtZWFuKGluanVyZWRfc2FtcGxlJFBsYXllci5BYnNvbHV0ZS5EaXN0KQ0KICBncm91cF9kaXN0YW5jZVtpLDJdID0gbWVhbih1bmluanVyZWRfc2FtcGxlJFBsYXllci5BYnNvbHV0ZS5EaXN0KQ0KICBncm91cF9kaXN0YW5jZVtpLDNdID0gZ3JvdXBfZGlzdGFuY2VbaSwxXSAtIGdyb3VwX2Rpc3RhbmNlW2ksMl0NCn0NCmBgYA0KDQoNCmBgYHtyIFBsb3R0aW5nIHJlc3VsdHMgZnJvbSBhdmVyYWdlZCBib29zdHJhcHBlZCBhYnNvbHV0ZSB2YWx1ZSBydW5uaW5nIGltYmFsYW5jZX0NCmdncGxvdChkYXRhID0gZ3JvdXBfZGlzdGFuY2UsIGFlcyhkaWZmX2luX2Rpc3QpKSArDQogIGdlb21faGlzdG9ncmFtKCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF9kaXN0YW5jZSRkaWZmX2luX2Rpc3QsIDAuMDUpLCBjb2xvcj0gIiNDRkI4N0MiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHF1YW50aWxlKGdyb3VwX2Rpc3RhbmNlJGRpZmZfaW5fZGlzdCwgMC45NSksIGNvbG9yPSAiI0NGQjg3QyIpICsNCiAgbGFicyh0aXRsZSA9ICJFc3RpbWF0ZWQgRGlmZmVyZW5jZSBpbiBBdmVyYWdlIEFic29sdXRlIFZhbHVlIiwgeD0iRGlmZmVyZW5jZSBpbiBBdmVyYWdlIEFic29sdXRlIFZhbHVlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90KGRhdGEgPSBncm91cF9kaXN0YW5jZSkgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoaW5qdXJlZF9kaXN0KSwgYWxwaGEgPSAwLjc1LCBmaWxsID0iI0NGQjg3QyIpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHVuaW5qdXJlZF9kaXN0KSwgYWxwaGEgPSAwLjc1KSArDQogIGxhYnModGl0bGUgPSAiRXN0aW1hdGVkIEF2ZXJhZ2UgQWJzb2x1dGUgVmFsdWUiLCB4PSJBdmVyYWdlIEFic29sdXRlIFZhbHVlIikgICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpgYGB7ciBSZW1vdmluZyB1bm5lY2Vzc2FyeSBvYmplY3RzIGZyb20gbG9vcHN9DQojcmVtb3ZpbmcganVuayB0aGF0IGNhbWUgZnJvbSB0aGUgbG9vcHMNCnJlbW92ZShpLHRlYW1fbWVhbiwgdGVhbV9zZCwgaW5qdXJlZF9zYW1wbGUsIHVuaW5qdXJlZF9zYW1wbGUsIGdyb3VwX3ZhcmlhbmNlcywgaW5qdXJlZF9kYXRhLCB1bmluanVyZWRfZGF0YSwgbWVhbl9wbGF5ZXJfdmFyaWFuY2VzLCBncm91cF9kaXN0YW5jZSkNCmBgYA0KDQoNCiMjIyBQb3NpdGlvbiBBbmFseXNpcw0KYGBge3Igc29ydGluZyBwbGF5ZXIgaW50byBnZW5lcmFsIHBvc2l0aW9uc30NCiNtYWtpbmcgbGlzdHMgdG8gc29ydCBwb3NpdGlvbiBpbnRvIGxhcmdlciBjYXRlZ29yaWVzDQpDT01CTyA8LSBjKCJRQiIsIkxCIiwiVEUiLCJSQiIsICJJTEIiKQ0KQklHIDwtIGMoIk9MIiwgIkRMIiwgIkRFIiwgIkRUIikNClNLSUxMIDwtIGMoIldSIiwgIkRCIiwgIkNCIiwgIlNBRiIpDQpQb3NpdGlvbnMgPC0gYygiQ09NQk8iLCAiQklHIiwgIlNLSUxMIikNCg0KI2dpdmluZyBwb3NpdGlvbnMgdG8gaW5jaWRlbnQgcmVwb3J0DQpJbmNpZGVudF9SZXBvcnRfY2xlYW4gPC0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuICU+JQ0KICBtdXRhdGUoU3BlY2lmaWMuUG9zaXRpb24gPSBQb3NpdGlvbiwNCiAgICAgICAgIFBvc2l0aW9uID0gY2FzZV93aGVuKFNwZWNpZmljLlBvc2l0aW9uICVpbiUgQ09NQk8gfiAiQ09NQk8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNwZWNpZmljLlBvc2l0aW9uICVpbiUgQklHIH4gIkJJRyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3BlY2lmaWMuUG9zaXRpb24gJWluJSBTS0lMTCB+ICJTS0lMTCIpKQ0KDQojZ2l2aW5nIHBvc2l0aW9ucyB0byBjYXRhcHVsdCBzZXNzaW9uDQpDYXRhcHVsdF9TZXNzaW9uX2NsZWFuIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW4gJT4lDQogIG11dGF0ZShTcGVjaWZpYy5Qb3NpdGlvbiA9IFByaW1hcnkuUG9zaXRpb24sDQogICAgICAgICBQb3NpdGlvbiA9IGNhc2Vfd2hlbihQcmltYXJ5LlBvc2l0aW9uICVpbiUgQ09NQk8gfiAiQ09NQk8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFByaW1hcnkuUG9zaXRpb24gJWluJSBCSUcgfiAiQklHIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQcmltYXJ5LlBvc2l0aW9uICVpbiUgU0tJTEwgfiAiU0tJTEwiKSkNCg0KI29ubHkgdGFraW5nIElEcyBhbmQgcG9zaXRpb24gbmFtZXMgYW5kIGNhdGVnb3JpZXMNCmluY2lkZW50X2luZm8gPC0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuWyxjKCJhbm9uX2lkIiwgIlBvc2l0aW9uIiwgIlNwZWNpZmljLlBvc2l0aW9uIildDQpjYXRhcHVsdF9pbmZvIDwtIENhdGFwdWx0X1Nlc3Npb25fY2xlYW5bLGMoImFub25faWQiLCAiUG9zaXRpb24iLCAiU3BlY2lmaWMuUG9zaXRpb24iKV0NCg0KI2NvbXByZWhlbnNpdmUgbGlzdCBvZiBJRHMsIHRoZWlyIHBvc2l0aW9uLCBhbmQgY2F0ZWdvcnkNCmluZm8gPC0gZGlzdGluY3QocmJpbmQoaW5jaWRlbnRfaW5mbywgY2F0YXB1bHRfaW5mbykpDQoNCiNhZGQgdGhpcyBvbmx5IGhpc3RvcmljYWwgcnVubmluZw0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIGxlZnRfam9pbihIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4sIGluZm8sIGJ5PSJhbm9uX2lkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVsYXRpb25zaGlwPSJtYW55LXRvLW1hbnkiKSAlPiUNCiAgI2NhbGN1bGF0aW5nIGVhY2ggcGxheWVyJ3MgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UNCiAgZ3JvdXBfYnkoYW5vbl9pZCkgJT4lDQogIG11dGF0ZShQbGF5ZXIuVmFyaWFuY2UgPSB2YXIoUnVubmluZy5JbWJhbGFuY2UpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KDQpgYGB7ciBMb29raW5nIGF0IFJ1bm5pbmcgSW1iYWxhbmNlIH0NCmZvcihpIGluIDE6Myl7DQogIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRQb3NpdGlvbj09UG9zaXRpb25zW2ldLF0sYWVzKFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICAgIGdlb21faGlzdG9ncmFtKCkgKw0KICAgIGxhYnMoc3VidGl0bGU9UG9zaXRpb25zW2ldKQ0KICANCiAgcHJpbnQocCkNCn0NCmBgYA0KDQpgYGB7cn0NClBsYXllcl9TdW1tYXJ5X1N0YXRzIDwtIGRpc3RpbmN0KEhpc3RvcmljYWxfUnVubmluZ19jbGVhblssYygiYW5vbl9pZCIsICJQb3NpdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNwZWNpZmljLlBvc2l0aW9uIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBsYXllci5WYXJpYW5jZSIpXSkNCmBgYA0KDQoNCmBgYHtyfQ0KZm9yKGkgaW4gMTozKXsNCiAgcCA8LSBnZ3Bsb3QoZGF0YT1QbGF5ZXJfU3VtbWFyeV9TdGF0c1tQbGF5ZXJfU3VtbWFyeV9TdGF0cyRQb3NpdGlvbj09UG9zaXRpb25zW2ldLF0sDQogICAgICAgICAgICAgIGFlcyhQbGF5ZXIuVmFyaWFuY2UpKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oKSArDQogICAgbGFicyhzdWJ0aXRsZT1Qb3NpdGlvbnNbaV0pDQogIA0KICBwcmludChwKQ0KfQ0KYGBgDQoNCg0KIyMjIyBCb290c3RyYXBwaW5nIERpZmZlcmVudCBQb3NpdGlvbnMNCmBgYHtyfQ0KI3NwbGl0dGluZyB1cCBkYXRhIHNldCBpbnRvIHRoZSBkaWZmZXJlbnQgY2F0ZWdvcmllcw0KQ09NQk9TIDwtIFBsYXllcl9TdW1tYXJ5X1N0YXRzICU+JQ0KICBmaWx0ZXIoUG9zaXRpb24gPT0gIkNPTUJPIikgJT4lDQogIG5hLm9taXQoKQ0KDQpTS0lMTFMgPC0gUGxheWVyX1N1bW1hcnlfU3RhdHMgJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiU0tJTEwiKSAlPiUNCiAgbmEub21pdCgpDQoNCkJJR1MgPC0gUGxheWVyX1N1bW1hcnlfU3RhdHMgJT4lDQogIGZpbHRlcihQb3NpdGlvbiA9PSAiQklHIikgJT4lDQogIG5hLm9taXQoKQ0KDQpncm91cF9hdmdfdmFyaWFuY2UgPC0gZGF0YS5mcmFtZShDT01CT192YXIgPSByZXAoTkEsIDUwMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU0tJTExfdmFyID0gcmVwKE5BLCA1MDAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJJR192YXIgPSByZXAoTkEsIDUwMDApKQ0KYGBgDQoNCg0KYGBge3J9DQpmb3IoaSBpbiAxOjUwMDApew0KICBzZXQuc2VlZChpKQ0KICBjb21ib19zYW1wbGUgPC0gc2FtcGxlX24oQ09NQk9TLCBzaXplPTIwLCByZXBsYWNlPVRSVUUpDQogIHNraWxsX3NhbXBsZSA8LSBzYW1wbGVfbihTS0lMTFMsIHNpemU9MjIsIHJlcGxhY2U9VFJVRSkNCiAgYmlnX3NhbXBsZSA8LSBzYW1wbGVfbihCSUdTLCBzaXplPTI0LCByZXBsYWNlPVRSVUUpDQogIA0KICBncm91cF9hdmdfdmFyaWFuY2VbaSwxXSA8LSBtZWFuKG5hLm9taXQoY29tYm9fc2FtcGxlJFBsYXllci5WYXJpYW5jZSkpDQogIGdyb3VwX2F2Z192YXJpYW5jZVtpLDJdIDwtIG1lYW4obmEub21pdChza2lsbF9zYW1wbGUkUGxheWVyLlZhcmlhbmNlKSkNCiAgZ3JvdXBfYXZnX3ZhcmlhbmNlW2ksM10gPC0gbWVhbihuYS5vbWl0KGJpZ19zYW1wbGUkUGxheWVyLlZhcmlhbmNlKSkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9Z3JvdXBfYXZnX3ZhcmlhbmNlLCBhZXMoQ09NQk9fdmFyKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJENPTUJPX3ZhciwgMC4wNSkpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJENPTUJPX3ZhciwgMC45NSkpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmdncGxvdChkYXRhPWdyb3VwX2F2Z192YXJpYW5jZSwgYWVzKFNLSUxMX3ZhcikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0iI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJFNLSUxMX3ZhciwgMC4wNSksIGNvbG9yPSIjQ0ZCODdDIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkU0tJTExfdmFyLCAwLjk1KSwgY29sb3I9IiNDRkI4N0MiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3QoZGF0YT1ncm91cF9hdmdfdmFyaWFuY2UsIGFlcyhCSUdfdmFyKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsPSIjQTJBNEEzIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQklHX3ZhciwgMC4wNSksIGNvbG9yPSIjQTJBNEEzIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQklHX3ZhciwgMC45NSksIGNvbG9yPSIjQTJBNEEzIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KZ2dwbG90KGRhdGE9Z3JvdXBfYXZnX3ZhcmlhbmNlKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyhDT01CT192YXIpLCBhbHBoYT0wLjUsIGZpbGw9ImJsYWNrIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoU0tJTExfdmFyKSwgYWxwaGE9MC41LCBmaWxsPSIjQ0ZCODdDIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoQklHX3ZhciksIGFscGhhPTAuNSwgZmlsbD0iI0EyQTRBMyIpICsNCiAgbGFicyh0aXRsZT0iRXN0aW1hdGVkIEF2ZXJhZ2UgVmFyaWFuY2UgaW4gUnVubmluZyBJbWJhbGFuY2UiLCB4PSJBdmVyYWdlIFZhcmlhbmNlIGluIFJ1bm5pbmcgSW1iYWxhbmNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KYGBge3J9DQp2YXJfY29uZmludHMgPC0gZGF0YS5mcmFtZShDYXRlZ29yeSA9IGMoIkNPTUJPIiwgIlNLSUxMIiwgIkJJRyIpLA0KICAgICAgICAgICAgICAgICAgICAgICBMb3dlcl9Cb3VuZCA9IGMocXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJENPTUJPX3ZhciwgMC4wNSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJFNLSUxMX3ZhciwgMC4wNSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJEJJR192YXIsIDAuMDUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgTWVkaWFuID0gYyhxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkQ09NQk9fdmFyLCAwLjUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlKGdyb3VwX2F2Z192YXJpYW5jZSRTS0lMTF92YXIsIDAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJEJJR192YXIsIDAuNSkpLA0KICAgICAgICAgICAgICAgICAgICAgICBVcHBlcl9Cb3VuZCA9IGMocXVhbnRpbGUoZ3JvdXBfYXZnX3ZhcmlhbmNlJENPTUJPX3ZhciwgMC45NSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfdmFyaWFuY2UkU0tJTExfdmFyLCAwLjk1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlKGdyb3VwX2F2Z192YXJpYW5jZSRCSUdfdmFyLCAwLjk1KSkpDQpoZWFkKHZhcl9jb25maW50cykNCg0KZ2dwbG90KGRhdGE9dmFyX2NvbmZpbnRzLCBhZXMoeD1DYXRlZ29yeSwgeT1NZWRpYW4sIHltaW49TG93ZXJfQm91bmQsIHltYXg9VXBwZXJfQm91bmQsIGNvbG9yPUNhdGVnb3J5KSkgKw0KICBnZW9tX3BvaW50cmFuZ2UoKSArDQogIGxhYnMoeT0iRXN0aW1hdGVkIEF2ZXJhZ2UgVmFyaWFuY2UgaW4gUnVubmluZyBJbWJhbGFuY2UiLCB0aXRsZT0iUnVubmluZyBJbWJhbGFuY2UgVmFyaWFuY2UgOTAlIENJIikgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiQ09NQk8iID0gImJsYWNrIiwgIlNLSUxMIiA9ICIjQ0ZCODdDIiwgIkJJRyIgPSAiI0EyQTRBMyIpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpgYGB7cn0NCkhpc3RvcmljYWxfUnVubmluZ19jbGVhbiA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIG11dGF0ZShBYnMuVmFsdWUuUnVubmluZy5JbWJhbGFuY2UgPSBhYnMoUnVubmluZy5JbWJhbGFuY2UpKQ0KDQpDT01CT1MgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBmaWx0ZXIoUG9zaXRpb24gPT0gIkNPTUJPIikNCg0KU0tJTExTIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZmlsdGVyKFBvc2l0aW9uID09ICJTS0lMTCIpDQoNCkJJR1MgPC0gSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuICU+JQ0KICBmaWx0ZXIoUG9zaXRpb24gPT0gIkJJRyIpDQoNCmdyb3VwX2F2Z19kaXN0IDwtIGRhdGEuZnJhbWUoQ09NQk9fZGlzdCA9IHJlcChOQSwgNTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNLSUxMX2Rpc3QgPSByZXAoTkEsIDUwMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCSUdfZGlzdCA9IHJlcChOQSwgNTAwMCkpDQpgYGANCg0KYGBge3J9DQpmb3IoaSBpbiAxOjUwMDApew0KICBzZXQuc2VlZChpKQ0KICBjb21ib19zYW1wbGUgPC0gc2FtcGxlX24oQ09NQk9TLCBzaXplPTEyMDMsIHJlcGxhY2U9VFJVRSkNCiAgc2tpbGxfc2FtcGxlIDwtIHNhbXBsZV9uKFNLSUxMUywgc2l6ZT0xNjM1LCByZXBsYWNlPVRSVUUpDQogIGJpZ19zYW1wbGUgPC0gc2FtcGxlX24oQklHUywgc2l6ZT0xMjI0LCByZXBsYWNlPVRSVUUpDQogIA0KICBncm91cF9hdmdfZGlzdFtpLDFdIDwtIG1lYW4obmEub21pdChjb21ib19zYW1wbGUkQWJzLlZhbHVlLlJ1bm5pbmcuSW1iYWxhbmNlKSkNCiAgZ3JvdXBfYXZnX2Rpc3RbaSwyXSA8LSBtZWFuKG5hLm9taXQoc2tpbGxfc2FtcGxlJEFicy5WYWx1ZS5SdW5uaW5nLkltYmFsYW5jZSkpDQogIGdyb3VwX2F2Z19kaXN0W2ksM10gPC0gbWVhbihuYS5vbWl0KGJpZ19zYW1wbGUkQWJzLlZhbHVlLlJ1bm5pbmcuSW1iYWxhbmNlKSkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGE9Z3JvdXBfYXZnX2Rpc3QpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKENPTUJPX2Rpc3QpLCBhbHBoYT0wLjUsIGZpbGw9ImJsYWNrIikgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoU0tJTExfZGlzdCksIGFscGhhPTAuNSwgZmlsbD0iI0NGQjg3QyIpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKEJJR19kaXN0KSwgYWxwaGE9MC41LCBmaWxsPSIjQTJBNEEzIikgKw0KICBsYWJzKHRpdGxlID0gIkVzdGltYXRlZCBBdmVyYWdlIEFic29sdXRlIFZhbHVlIFJ1bm5pbmcgSW1iYWxhbmNlIiwgeD0iQXZlcmFnZSBBYnNvbHV0ZSBWYWx1ZSBSdW5uaW5nIEltYmFsYW5jZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KZGlzdF9jb25maW50cyA8LSBkYXRhLmZyYW1lKENhdGVnb3J5ID0gYygiQ09NQk8iLCAiU0tJTEwiLCAiQklHIiksDQogICAgICAgICAgICAgICAgICAgICAgIExvd2VyX0JvdW5kID0gYyhxdWFudGlsZShncm91cF9hdmdfZGlzdCRDT01CT19kaXN0LCAwLjA1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfZGlzdCRTS0lMTF9kaXN0LCAwLjA1KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfZGlzdCRCSUdfZGlzdCwgMC4wNSkpLA0KICAgICAgICAgICAgICAgICAgICAgICBNZWRpYW4gPSBjKHF1YW50aWxlKGdyb3VwX2F2Z19kaXN0JENPTUJPX2Rpc3QsIDAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUoZ3JvdXBfYXZnX2Rpc3QkU0tJTExfZGlzdCwgMC41KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShncm91cF9hdmdfZGlzdCRCSUdfZGlzdCwgMC41KSksDQogICAgICAgICAgICAgICAgICAgICAgIFVwcGVyX0JvdW5kID0gYyhxdWFudGlsZShncm91cF9hdmdfZGlzdCRDT01CT19kaXN0LCAwLjk1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlKGdyb3VwX2F2Z19kaXN0JFNLSUxMX2Rpc3QsIDAuOTUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUoZ3JvdXBfYXZnX2Rpc3QkQklHX2Rpc3QsIDAuOTUpKSkNCmhlYWQoZGlzdF9jb25maW50cykNCg0KZ2dwbG90KGRhdGE9ZGlzdF9jb25maW50cywgYWVzKHg9Q2F0ZWdvcnksIHk9TWVkaWFuLCB5bWluPUxvd2VyX0JvdW5kLCB5bWF4PVVwcGVyX0JvdW5kLCBjb2xvcj1DYXRlZ29yeSkpICsNCiAgZ2VvbV9wb2ludHJhbmdlKCkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiQ09NQk8iID0gImJsYWNrIiwgIlNLSUxMIiA9ICIjQ0ZCODdDIiwgIkJJRyIgPSAiI0EyQTRBMyIpKSArDQogIGxhYnModGl0bGU9IkF2ZXJhZ2UgQWJzb2x1dGUgVmFsdWUgUnVubmluZyBJbWJhbGFuY2UgOTAlIENJIiwgeT0iRXN0aW1hdGVkIEF2ZXJhZ2UgQWJzb2x1dGUgVmFsdWUgUnVubmluZyBJbWJhbGFuY2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KYGBge3J9DQpJbmp1cnlfSW5jaWRlbnRzIDwtIGRpc3RpbmN0KEluY2lkZW50X1JlcG9ydF9jbGVhblssYygiYW5vbl9pZCIsICJQb3NpdGlvbiIsICJEYXRlLm9mLkluanVyeSIpXSkNCg0KUG9zaXRpb25fY291bnRzIDwtIG5hLm9taXQoZGlzdGluY3QoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuWyxjKCJhbm9uX2lkIiwgIlBvc2l0aW9uIildKSkNCg0KZ2dwbG90KEluanVyeV9JbmNpZGVudHMsIGFlcyhQb3NpdGlvbiwgZmlsbD1Qb3NpdGlvbikpICsNCiAgZ2VvbV9iYXIoKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkNPTUJPIiA9ICJibGFjayIsICJTS0lMTCIgPSAiI0NGQjg3QyIsICJCSUciID0gIiNBMkE0QTMiKSkgKw0KICBsYWJzKHRpdGxlPSJIYW1zdHJpbmcgSW5qdXJpZXMgYnkgUG9zaXRpb24iKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnZ3Bsb3QoUG9zaXRpb25fY291bnRzLCBhZXMoUG9zaXRpb24sIGZpbGw9UG9zaXRpb24pKSArDQogIGdlb21fYmFyKCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJDT01CTyIgPSAiYmxhY2siLCAiU0tJTEwiID0gIiNDRkI4N0MiLCAiQklHIiA9ICIjQTJBNEEzIikpICsNCiAgbGFicyh0aXRsZT0iVG90YWwgUG9zaXRpb24gQ291bnRzIG9uIFRlYW0iKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCmBgYHtyIFJlbW92aW5nIHVubmVjZXNzYXJ5IG9iamVjdHMgZnJvbSBlbnZpcm9ubWVudH0NCnJlbW92ZShiaWdfc2FtcGxlLCBCSUdTLCBjYXRhcHVsdF9pbmZvLCBjb21ib19zYW1wbGUsIENPTUJPUywgY29uZmludHMsIGdyb3VwX2F2Z192YXJpYW5jZSwNCiAgICAgICBpbmNpZGVudF9pbmZvLCBpbmZvLCBwLCBQbGF5ZXJfU3VtbWFyeV9TdGF0cywgc2tpbGxfc2FtcGxlLCBTS0lMTFMsIEJJRywgQ09NQk8sIGksDQogICAgICAgUG9zaXRpb25zLCBTS0lMTCwgZGlzdF9jb25maW50cywgZ3JvdXBfYXZnX3ZhcmlhbmNlLCBncm91cF9hdmdfZGlzdCwgZ3JvdXBfZGlzdGFuY2UsIA0KICAgICAgIGdyb3VwX3ZhcmlhbmNlcywgaW5qdXJlZF9kYXRhLCBpbmp1cmVkX3NhbXBsZSwgSW5qdXJ5X0luY2lkZW50cywgbWVhbl9wbGF5ZXJfdmFyaWFuY2VzLCANCiAgICAgICBQb3NpdGlvbl9jb3VudHMsIHZhcl9jb25maW50cykNCmBgYA0KDQoNCiMjIFdoYXQgaXMgYSBtZWFuaW5nZnVsIGNoYW5nZT8gV2hhdCByZWQgZmxhZ3Mgc2hvdWxkIGdvIG9mZiB3aGVuIHdlIHNlZSBhIHdlZWstdG8td2VlayBjaGFuZ2UgaW4gcnVubmluZyBpbWJhbGFuY2U/DQoNCiAgQmFzZWQgb24gdGhlIGFuYWx5c2lzIGJlbG93LCB0aGVyZSBkb2Vzbid0IHNlZW0gdG8gYmUgYW55IG1ham9yIGRpc2Nlcm5pYmxlIGRpZmZlcmVuY2VzIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGJlZm9yZSBvciBmb2xsb3dpbmcgYW4gaW5qdXJ5LiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlcmUgbWF5IG5vdCBiZSBhIGRpcmVjdCBsaW5rIGJldHdlZW4gSFNJIHJpc2sgYW5kIHJ1bm5pbmcgaW1iYWxhbmNlIHZhbHVlIGRpcmVjdGx5IGJlZm9yZWhhbmQuIEluc3RlYWQsIHRoZSB0cmVuZHMgb2YgbWFueSBwbGF5ZXJzIHdobyB3ZXJlIGluanVyZWQgc2VlbXMgdG8gaGF2ZSBhIHJlbGF0aXZlbHkgY29uc2lzdGVudCB0cmVuZCBub3QgZW50aXJlbHkgZGVwZW5kZW50IG9uIHRpbWUuIA0KICBMb29raW5nIGF0IHN1bW1hcnkgc3RhdGlzdGljcyBvZiBydW5uaW5nIGltYmFsYW5jZSBpbiB0aGUgd2Vla3MgbGVhZGluZyB1cCB0byBhbmQgZm9sbG93aW5nIGEgaGFtc3RyaW5nIGluanVyeSwgdGhlcmUgYXJlIGFsc28gbm8gZ2xhcmluZyB0cmVuZHMuIEZvciB0aGlzIGFuYWx5c2lzLCB3ZSBsb29rZWQgYXQgdGhlIG1lYW4gYW5kIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIHBlciB3ZWVrIGxlYWRpbmcgdXAgdG8gYW5kIGFmdGVyIGFuIGluanVyeSBmb3IgYWxsIG9mIHRoZSBpbmp1cmVkIHBsYXllcnMgd2l0aCBydW5uaW5nIGltYmFsYW5jZSBkYXRhLiBUaGlzIHNob3dlZCB1cyB0aGF0IHRoZXJlIGlzIG5vIGNsZWFyIGluZGljYXRvciBvZiBIU0kgcmlzayBpbiBydW5uaW5nIGltYmFsYW5jZSBvciBhbnkgc3VtbWFyeSBzdGF0aXN0aWMgb2YgaXQuIEluc3RlYWQgaXQgbWF5IGJlIG1vcmUgdXNlZnVsIHRvIGxvb2sgYXQgZWFjaCBwbGF5ZXIncyB0b3RhbCBydW5uaW5nIGltYmFsYW5jZSBhbmQgdGhlaXIgaW5kaXZpZHVhbCB2YXJpYW5jZS4gVGhpcyBzZWVtcyB0byBiZSBtb3JlIG9mIGEgdXNlZnVsIHRvb2wgZm9yIGRpZmZlcmVudGlhdGluZyBiZXR3ZWVuIGluanVyZWQgYW5kIHVuaW5qdXJlZCBhdGhsZXRlcy4NCg0KYGBge3IgQ2FsY3VsYXRpbmcgd2Vla3MgYmVmb3JlIGFuZCBhZnRlciBpbmp1cnkgb2NjdXJhbmNlIGJhc2VkIG9uIGRhdGUsIGhvdyBtYW55IGluanVyaWVzIHBsYXllciBoYXN9DQojZ2V0dGluZyBydW5uaW5nIGltYmFsYW5jZXMgZm9yIGp1c3QgaW5qdXJlZCBwbGF5ZXJzDQpJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyA8LSBIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4gJT4lDQogIGZpbHRlcihhbm9uX2lkICVpbiUgaW5qdXJlZF9JRHMpDQoNCiNtYWtpbmcgbmV3IGNvbHVtbiB0byByZXByZXNlbnQgd2hlbiBpbiB0aW1lIGluanVyeSB3b3VsZCBiZSwgbmVnYXRpdmUgbWVhbnMgYmVmb3JlIGluanVyeSBhbmQgcG9zaXRpdmUgbWVhbnMgYWZ0ZXIgaW5qdXJ5LCAwIG1lYW5zIGRhdGUgb2YgaW5qdXJ5IGlmIHRoZXJlJ3MgZGF0YSBmb3IgdGhhdCBkYXkNCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJFdlZWtzLkFmdGVyLkluanVyeSA8LSByZXAoTkEsIDE2NTgpDQpJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRJbmp1cnkuQ291bnQgPC0gcmVwKE5BLCAxNjU4KQ0KDQojbWFraW5nIG5ldyBjb2x1bW4gaW4gaW5jaWRlbnQgcmVwb3J0IGZvciB0aGUgaW5qdXJ5IGNvdW50DQpJbmNpZGVudF9SZXBvcnRfY2xlYW4kSW5qdXJ5LkNvdW50IDwtIHJlcChOQSwgMTIyKQ0KDQojZ28gdGhyb3VnaCBhbGwgb2YgdGhlIGluanVyZWQgcGxheWVycyBpbiB0aGUgZGF0YSBzZXQNCmZvcihpIGluIDE6MjIpew0KICAjZ2V0IHRoZSBkYXRlcyBlYWNoIHBsYXllciB3YXMgaW5qdXJlZA0KICBpbmp1cnlfZGF0ZXMgPC0gdW5pcXVlKEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkNCiAgDQogICNnbyB0aHJvdWdoIGFsbCBvZiB0aGUgZGF0ZXMgaW4gd2hpY2ggdGhlIHBsYXllciBoYWQgYW4gaW5qdXJ5DQogIGZvcihqIGluIDE6bGVuZ3RoKGluanVyeV9kYXRlcykpew0KICAgIA0KICAgICNjYWxjdWxhdGUgZGF0ZXMgZm9yIDEsIDIsIDMsIDQgd2Vla3MgYmVmb3JlIGFuZCBhZnRlciBlYWNoIGluanVyeSBkYXRlDQogICAgcGFzdF8xIDwtIGluanVyeV9kYXRlc1tqXS03DQogICAgcGFzdF8yIDwtIGluanVyeV9kYXRlc1tqXS0xNA0KICAgIHBhc3RfMyA8LSBpbmp1cnlfZGF0ZXNbal0tMjENCiAgICBwYXN0XzQgPC0gaW5qdXJ5X2RhdGVzW2pdLTI4DQogICAgZnV0dXJlXzEgPC0gaW5qdXJ5X2RhdGVzW2pdKzcNCiAgICBmdXR1cmVfMiA8LSBpbmp1cnlfZGF0ZXNbal0rMTQNCiAgICBmdXR1cmVfMyA8LSBpbmp1cnlfZGF0ZXNbal0rMjENCiAgICBmdXR1cmVfNCA8LSBpbmp1cnlfZGF0ZXNbal0rMjgNCiAgICANCiAgICAjQ2FsY3VsYXRpbmcgaG93IG1hbnkgaW5qdXJpZXMgdGhpcyBpcyBmb3IgdGhlIHBsYXllcg0KICAgIGluanVyeV9jb3VudCA8LSBhcy5jaGFyYWN0ZXIoKGxlbmd0aChpbmp1cnlfZGF0ZXMpKSAtIGogKyAxKQ0KICAgIA0KICAgICNjb21wYXJlIGRhdGUgb2YgZGF0YSBwb2ludCBmb3IgZWFjaCBwbGF5ZXIgdG8gZGF0ZSBvZiBpbmp1cnksIHN0b3JlIGluIFdlZWtzLkFmdGVyLkluanVyeSBjb2x1bW4sIHN0b3JlIGluanVyeSBjb3VudA0KICAgIA0KICAgICNmaXJzdCB3ZWVrIGFmdGVyIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBpbmp1cnlfZGF0ZXNbal0gJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV8xLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICIxIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBpbmp1cnlfZGF0ZXNbal0gJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV8xLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICAgICNzZWNvbmQgd2VlayBhZnRlciBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gZnV0dXJlXzEgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV8yLF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICIyIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBmdXR1cmVfMSAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzIsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgDQogICAgI3RoaXJkIHdlZWsgYWZ0ZXIgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+IGZ1dHVyZV8yICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPD1mdXR1cmVfMyxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiMyINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gZnV0dXJlXzIgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV8zLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICAgICNmb3VydGggd2VlayBhZnRlciBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlID4gZnV0dXJlXzMgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU8PWZ1dHVyZV80LF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICI0Ig0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPiBmdXR1cmVfMyAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZTw9ZnV0dXJlXzQsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50ICAgIA0KICAgICN3ZWVrIHJpZ2h0IGJlZm9yZSBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgaW5qdXJ5X2RhdGVzW2pdICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzEsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIi0xIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBpbmp1cnlfZGF0ZXNbal0gJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfMSxdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICAjdHdvIHdlZWtzIGJlZm9yZSBpbmp1cnkNCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgcGFzdF8xICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzIsXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIi0yIg0KICAgIA0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBwYXN0XzEgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfMixdJEluanVyeS5Db3VudCA8LSBpbmp1cnlfY291bnQNCiAgICANCiAgICAjdGhyZWUgd2Vla3MgYmVmb3JlIGluanVyeQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGUgPCBwYXN0XzIgJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJERhdGU+PXBhc3RfMyxdJFdlZWtzLkFmdGVyLkluanVyeSA8LSAiLTMiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IHBhc3RfMiAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF8zLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgICAgICANCiAgICAjZm91ciB3ZWVrcyBiZWZvcmUgaW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA8IHBhc3RfMyAmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZT49cGFzdF80LF0kV2Vla3MuQWZ0ZXIuSW5qdXJ5IDwtICItNCINCiAgICANCiAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZ1tJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDwgcGFzdF8zICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlPj1wYXN0XzQsXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgDQogICAgI0RhdGUgb2YgSW5qdXJ5DQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA9PSBpbmp1cnlfZGF0ZXNbal0sXSRXZWVrcy5BZnRlci5Jbmp1cnkgPC0gIjAiDQogICAgDQogICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmdbSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0gJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA9PSBpbmp1cnlfZGF0ZXNbal0sXSRJbmp1cnkuQ291bnQgPC0gaW5qdXJ5X2NvdW50DQogICAgDQogICAgDQogICAgI2FkZGluZyBpbmp1cnkgY291bnQgdG8gaW5kaWNlbnQgcmVwb3J0DQogICAgSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmIEluY2lkZW50X1JlcG9ydF9jbGVhbiREYXRlLm9mLkluanVyeT09aW5qdXJ5X2RhdGVzW2pdLF0kSW5qdXJ5LkNvdW50IDwtIGluanVyeV9jb3VudA0KICAgIA0KICB9DQp9DQoNCg0KI21ha2luZyB3ZWVrcyBhZnRlciBpbmp1cnkgYW5kIGluanVyeSBjb3VudCBpbnRvIGEgZmFjdG9yIGFuZCBjb21iaW5pbmcgZGF0YSBzZXRzIGJhY2sgdG9nZXRoZXINCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nIDwtIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nICU+JQ0KICBtdXRhdGUoV2Vla3MuQWZ0ZXIuSW5qdXJ5ID0gZmFjdG9yKFdlZWtzLkFmdGVyLkluanVyeSksDQogICAgICAgICBJbmp1cnkuQ291bnQgPSBmYWN0b3IoSW5qdXJ5LkNvdW50KSkNCg0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIGxlZnRfam9pbihIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4sIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nKQ0KDQojZ2V0dGluZyByaWQgb2YganVuayB0aGF0IHdhcyBmcm9tIHRoZSBsb29wDQpyZW1vdmUoZnV0dXJlXzEsIGZ1dHVyZV8yLCBmdXR1cmVfMywgZnV0dXJlXzQsIGksIGluanVyeV9jb3VudCwgaW5qdXJ5X2RhdGVzLCBqLCBwYXN0XzEsIHBhc3RfMiwgcGFzdF8zLCBwYXN0XzQpDQpyZW1vdmUoSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcpDQpgYGANCg0KIyMjIEluanVyeSBSaXNrDQpgYGB7cn0NCmZvcihpIGluIDE6MjIpew0KICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSwgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICAgIGdlb21fbGluZShsaW5ldHlwZT0xKSArDQogICAgZ2VvbV9wb2ludChhZXMoY29sb3I9V2Vla3MuQWZ0ZXIuSW5qdXJ5KSkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSwgbGluZXR5cGU9MikgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCItNCI9ImdyZWVuIiwgIi0zIj0ieWVsbG93IiwgIi0yIj0ib3JhbmdlIiwgIi0xIj0icmVkIiwgIjAiPSJibGFjayIsIjEiPSAicHVycGxlIiwgIjIiPSJuYXZ5IiwgIjMiPSJibHVlIiwgIjQiPSJza3libHVlIikpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIGxhYnModGl0bGU9IlJ1bm5pbmcgSW1iYWxhbmNlIiwgc3VidGl0bGUgPSBpbmp1cmVkX0lEc1tpXSkNCiAgDQogIHByaW50KHApDQp9DQpgYGANCg0KDQpgYGB7cn0NCiNtYWtpbmcgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIHJ1bm5pbmcgaW1iYWxhbmNlIHBlciB3ZWVrIHJlbGF0aXZlIHRvIGluanVyeQ0KSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgSW5qdXJ5LkNvdW50LCBXZWVrcy5BZnRlci5Jbmp1cnkpICU+JQ0KICBtdXRhdGUoV2Vla3MuQWZ0ZXIuSW5qdXJ5LlZhcmlhYmlsaXR5ID0gdmFyKFJ1bm5pbmcuSW1iYWxhbmNlKSwNCiAgICAgICAgIFdlZWtzLkFmdGVyLkluanVyeS5NZWFuID0gbWVhbihhYnMoUnVubmluZy5JbWJhbGFuY2UpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCmZvcihpIGluIDE6MjIpew0KICAjbG9va2luZyBhdCBtZWFuIHJ1bm5pbmcgaW1iYWxhbmNlIHBlciB3ZWVrIGJlZm9yZSBhbmQgYWZ0ZXIgaW5qdXJ5IGZvciBlYWNoIGluanVyZWQgcGxheWVyDQogIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGluanVyZWRfSURzW2ldLF0sIGFlcyhEYXRlLCBXZWVrcy5BZnRlci5Jbmp1cnkuTWVhbiwgZ3JvdXA9SW5qdXJ5LkNvdW50KSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yPVdlZWtzLkFmdGVyLkluanVyeSkpICsNCiAgICB4bGltKG1pbihJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpLTMwLCBtYXgoSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJERhdGUub2YuSW5qdXJ5KSszMCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpICsNCiAgICBsYWJzKHRpdGxlPSJBdmVyYWdlIEFic29sdXRlIERpc3RhbmNlIHBlciBXZWVrIixpbmp1cmVkX0lEc1tpXSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCItNCI9ImdyZWVuIiwgIi0zIj0ieWVsbG93IiwgIi0yIj0ib3JhbmdlIiwgIi0xIj0icmVkIiwgIjAiPSJibGFjayIsIjEiPSAicHVycGxlIiwgIjIiPSJuYXZ5IiwgIjMiPSJibHVlIiwgIjQiPSJza3libHVlIikpDQogIA0KICBwcmludChwKQ0KfQ0KDQpgYGANCg0KDQpgYGB7cn0NCmZvcihpIGluIDE6MjIpew0KICAjbG9va2luZyBhdCB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBwZXIgd2VlayBiZWZvcmUgYW5kIGFmdGVyIGluanVyeSBmb3IgZWFjaCBpbmp1cmVkIHBsYXllcg0KICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBpbmp1cmVkX0lEc1tpXSxdLCBhZXMoRGF0ZSwgV2Vla3MuQWZ0ZXIuSW5qdXJ5LlZhcmlhYmlsaXR5LCBncm91cD1Jbmp1cnkuQ291bnQpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9V2Vla3MuQWZ0ZXIuSW5qdXJ5KSkgKw0KICAgIHhsaW0obWluKEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSktMzAsIG1heChJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpKzMwKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSkgKw0KICAgIGxhYnModGl0bGU9IlZhcmlhbmNlIGluIFJ1bm5pbmcgSW1iYWxhbmNlIHBlciBXZWVrIiwgc3VidGl0bGU9aW5qdXJlZF9JRHNbaV0pICsNCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiLTQiPSJncmVlbiIsICItMyI9InllbGxvdyIsICItMiI9Im9yYW5nZSIsICItMSI9InJlZCIsICIwIj0iYmxhY2siLCIxIj0gInB1cnBsZSIsICIyIj0ibmF2eSIsICIzIj0iYmx1ZSIsICI0Ij0ic2t5Ymx1ZSIpKQ0KICANCiAgcHJpbnQocCkNCn0NCmBgYA0KDQojIyMgUGxheWVyIFRyZW5kcw0KYGBge3IgTG9va2luZyBhdCBkZXRlY3RhYmxlIHRyZW5kcyBpbiBwbGF5ZXIgZGF0YX0NCmZvcihpIGluIDE6NzEpew0KICAjb25seSBwbG90dGluZyBpZiB0aGVyZSBhcmUgb3ZlciAxNSBkYXRhIHBvaW50cyBmb3IgZWFjaCBwbGF5ZXINCiAgaWYobnJvdyhIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gYWxsX0lEc1tpXSxdKT4xNSl7DQogICAgI2NhbGN1bGF0aW5nIGhvdyBzdHJvbmcgb2YgYSBub24gbGluZWFyIHRyZW5kIHRoZXJlIGlzIHVzaW5nIGEgZ2FtDQogICAgZGYgPC0gc3VtbWFyeShnYW0oUnVubmluZy5JbWJhbGFuY2V+cyhEYXlzLlNpbmNlLlN0YXJ0KSwNCiAgICAgICAgICAgICAgZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gYWxsX0lEc1tpXSxdKSkkZWRmDQogICAgI2NhbGN1bGF0aW5nIGhvdyBzdHJvbmcgb2YgYSBsaW5lYXIgdHJlbmQgdGhlcmUgaXMgdXNpbmcga2VuZGFsbCBjb3JyZWxhdGlvbg0KICAgIEtlbmRhbGxfQ29yIDwtIGNvcih4PUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0kRGF5cy5TaW5jZS5TdGFydCwgeT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gYWxsX0lEc1tpXSxdJFJ1bm5pbmcuSW1iYWxhbmNlLCBtZXRob2Q9ImtlbmRhbGwiKQ0KICANCiAgICAjaWYgdGhlcmUgaXMgYSBkZXRlY3RhYmxlIGxpbmVhciByZWxhdGlvbnNoaXAgaW4gdGhlIGRhdGENCmlmKGFicyhLZW5kYWxsX0Nvcj4wLjIpICYgZGY8PTMpeyAjbGluZWFyIHRyZW5kIGluIGRhdGENCiAgcCA8LSBnZ3Bsb3QoZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gYWxsX0lEc1tpXSxdLCBhZXMoRGF0ZSwgUnVubmluZy5JbWJhbGFuY2UpKSArDQogICAgZ2VvbV9saW5lKCkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZ2VvbV9zbW9vdGgoY29sb3I9ICJyZWQiLA0KICAgICAgICAgICAgICAgIG1ldGhvZD0ibG0iLA0KICAgICAgICAgICAgICAgIHNlPUZBTFNFKSArDQogICAgbGFicyh0aXRsZT0iUnVubmluZyBJbWJhbGFuY2UgU2luY2UgSmFudWFyeSAxLCAyMDI0IChsaW5lYXIgdHJlbmQpIiwgc3VidGl0bGU9YWxsX0lEc1tpXSwpICsNCiAgICB4bGltKGFzLkRhdGUoIjIwMjUtMDEtMDEiLCAiJVktJW0tJWQiKSwgYXMuRGF0ZSgiMjAyNS0wNS0wMSIsICIlWS0lbS0lZCIpKQ0KICANCiAgcHJpbnQocCkNCn0NCiAgICAjaWYgdGhlcmUgaXMgYSBkZXRlY3RhYmxlIG5vbi1saW5lYXIgdHJlbmQgaW4gdGhlIGRhdGENCiAgaWYoZGYgPiAzKXsgI25vbiBsaW5lYXIgdHJlbmQgaW4gZGF0YQ0KICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgICBnZW9tX2xpbmUoKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBnZW9tX3Ntb290aChjb2xvciA9ICJza3libHVlIiwNCiAgICAgICAgICAgICAgICBzZT1GQUxTRSkgKw0KICAgIGxhYnModGl0bGU9IlJ1bm5pbmcgSW1iYWxhbmNlIFNpbmNlIEphbnVhcnkgMSwgMjAyNCAobm9uLWxpbmVhciB0cmVuZCkiLCBzdWJ0aXRsZT1hbGxfSURzW2ldLCkgKw0KICAgIHhsaW0oYXMuRGF0ZSgiMjAyNS0wMS0wMSIsICIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDI1LTA1LTAxIiwgIiVZLSVtLSVkIikpDQogIA0KICBwcmludChwKQ0KICB9DQogICAgI2lmIHRoZXJlIGlzIG5vIGRldGVjdGFibGUgdHJlbmQgaW4gdGhlIGRhdGENCiAgICBpZihkZjw9MyAmIGFicyhLZW5kYWxsX0Nvcik8PTAuMil7DQogICAgICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgICBnZW9tX2xpbmUoKSArDQogICAgZ2VvbV9wb2ludCgpICsNCiAgICBsYWJzKHRpdGxlPSJSdW5uaW5nIEltYmFsYW5jZSBTaW5jZSBKYW51YXJ5IDEsIDIwMjQgKG5vbi1saW5lYXIgdHJlbmQpIiwgc3VidGl0bGU9YWxsX0lEc1tpXSwpICsNCiAgICB4bGltKGFzLkRhdGUoIjIwMjUtMDEtMDEiLCAiJVktJW0tJWQiKSwgYXMuRGF0ZSgiMjAyNS0wNS0wMSIsICIlWS0lbS0lZCIpKQ0KICAgIH0NCiAgfQ0KfQ0KDQojcGxvdHRlZCBvbmx5IGZyb20gSmFudWFyeSAxLCAyMDI1IHRvIE1heSAxLCAyMDI1DQpgYGANCg0KDQojIyMgRGV0ZWN0YWJsZSBUcmVuZCB3aXRoIEluanVyeSBSaXNrDQpgYGB7cn0NCnRyZW5kcyA8LSBkYXRhLmZyYW1lKElEID0gYWxsX0lEcywNCiAgICAgICAgICAgICAgICAgICAgIEtDID0gcmVwKDAsIDcxKSkNCmZvcihpIGluIDE6NzEpew0KICAjb25seSBwbG90dGluZyBpZiB0aGVyZSBhcmUgb3ZlciAxNSBkYXRhIHBvaW50cyBmb3IgZWFjaCBwbGF5ZXINCiAgaWYobnJvdyhIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gYWxsX0lEc1tpXSxdKT4xNSl7DQogICAgI2NhbGN1bGF0aW5nIGhvdyBzdHJvbmcgb2YgYSBub24gbGluZWFyIHRyZW5kIHRoZXJlIGlzIHVzaW5nIGEgZ2FtDQogICAgZGYgPC0gc3VtbWFyeShnYW0oUnVubmluZy5JbWJhbGFuY2V+cyhEYXlzLlNpbmNlLlN0YXJ0KSwNCiAgICAgICAgICAgICAgZGF0YT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gYWxsX0lEc1tpXSxdKSkkZWRmDQogICAgI2NhbGN1bGF0aW5nIGhvdyBzdHJvbmcgb2YgYSBsaW5lYXIgdHJlbmQgdGhlcmUgaXMgdXNpbmcga2VuZGFsbCBjb3JyZWxhdGlvbg0KICAgIEtlbmRhbGxfQ29yIDwtIGNvcih4PUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0kRGF5cy5TaW5jZS5TdGFydCwgeT1IaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW5bSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuJGFub25faWQgPT0gYWxsX0lEc1tpXSxdJFJ1bm5pbmcuSW1iYWxhbmNlLCBtZXRob2Q9ImtlbmRhbGwiKQ0KICAgIA0KICAgIHRyZW5kc1t0cmVuZHMkSUQgPT0gYWxsX0lEc1tpXSwyXSA9IEtlbmRhbGxfQ29yDQogIA0KICAgIGlmKGFsbF9JRHNbaV0gJWluJSBpbmp1cmVkX0lEcyl7DQogICAgICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0sDQogICAgICAgICAgICAgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICAgICAgICBnZW9tX3BvaW50KGNvbG9yPSJza3libHVlIikgKw0KICAgICAgICBnZW9tX2xpbmUoY29sb3I9InNreWJsdWUiKSArDQogICAgICAgIGxhYnMoc3VidGl0bGU9S2VuZGFsbF9Db3IpDQogICAgfQ0KICAgIGlmKCEoYWxsX0lEc1tpXSAlaW4lIGluanVyZWRfSURzKSl7DQogICAgICBwIDwtIGdncGxvdChkYXRhPUhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBhbGxfSURzW2ldLF0sDQogICAgICAgICAgICAgYWVzKERhdGUsIFJ1bm5pbmcuSW1iYWxhbmNlKSkgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBnZW9tX2xpbmUoKSArDQogICAgICAgIGxhYnMoc3VidGl0bGU9S2VuZGFsbF9Db3IpDQogICAgfQ0KICAgIA0KICAgIHByaW50KHApDQogIH0NCn0NCg0KI3Bsb3R0ZWQgb25seSBmcm9tIEphbnVhcnkgMSwgMjAyNSB0byBNYXkgMSwgMjAyNQ0KYGBgDQoNCg0KYGBge3J9DQp0cmVuZHMgPC0gdHJlbmRzICU+JQ0KICBtdXRhdGUoaW5qdXJlZCA9IGlmZWxzZShJRCAlaW4lIGluanVyZWRfSURzLDEsMCkpDQoNCmdncGxvdCh0cmVuZHNbdHJlbmRzJEtDPjAgJiB0cmVuZHMkaW5qdXJlZD09MSxdLCBhZXMoS0MpKSArDQogIGdlb21faGlzdG9ncmFtKCkNCg0KZ2dwbG90KHRyZW5kc1t0cmVuZHMkS0M+MCAmIHRyZW5kcyRpbmp1cmVkPT0wLF0sIGFlcyhLQykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmluanVyZWRfdHJlbmRzIDwtIHRyZW5kcyAlPiUNCiAgZmlsdGVyKGluanVyZWQ9PTEpDQoNCnVuaW5qdXJlZF90cmVuZHMgPC0gdHJlbmRzICU+JQ0KICBmaWx0ZXIoaW5qdXJlZD09MCkNCg0KDQprZW5kYWxsX2NvcnMgPC0gZGF0YS5mcmFtZShpbmp1cmVkX2F2Z19LQyA9IHJlcChOQSw1MDAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaW5qdXJlZF9hdmdfS0MgPSByZXAoTkEsNTAwMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBhdmdfZGlmZl9LQyA9IHJlcChOQSw1MDAwKSkNCg0KZm9yKGkgaW4gMTo1MDAwKXsNCiAgc2V0LnNlZWQoaSkNCiAgaW5qdXJlZF9zYW1wIDwtIHNhbXBsZV9uKGluanVyZWRfdHJlbmRzLCBzaXplPTIyLCByZXBsYWNlPVRSVUUpDQogIHVuaW5qdXJlZF9zYW1wIDwtIHNhbXBsZV9uKHVuaW5qdXJlZF90cmVuZHMsIHNpemU9NDksIHJlcGxhY2U9VFJVRSkNCiAgDQogIGtlbmRhbGxfY29yc1tpLDFdIDwtIG1lYW4oYWJzKGluanVyZWRfc2FtcCRLQykpDQogIGtlbmRhbGxfY29yc1tpLDJdIDwtIG1lYW4oYWJzKHVuaW5qdXJlZF9zYW1wJEtDKSkNCiAga2VuZGFsbF9jb3JzW2ksM10gPC0ga2VuZGFsbF9jb3JzW2ksMV0gLSBrZW5kYWxsX2NvcnNbaSwyXQ0KfQ0KDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPWtlbmRhbGxfY29ycywgYWVzKGF2Z19kaWZmX0tDKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoa2VuZGFsbF9jb3JzJGF2Z19kaWZmX0tDLCAwLjA1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoa2VuZGFsbF9jb3JzJGF2Z19kaWZmX0tDLCAwLjk1KSwgY29sb3IgPSAiI0NGQjg3QyIpICsNCiAgbGFicyh0aXRsZSA9ICJFc3RpbWF0ZWQgRGlmZmVyZW5jZSBpbiBBdmVyYWdlIEtlbmRhbGwgUmFuayBDb3JyZWxhdGlvbiBDb2VmZmljaWVudCIsDQogICAgICAgeCA9ICJEaWZmZXJlbmNlIGluIEF2ZXJhZ2UgS2VuZGFsbCBDb2VmZmljaWVudCIpDQoNCmdncGxvdChkYXRhPWtlbmRhbGxfY29ycykgKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoaW5qdXJlZF9hdmdfS0MpLCBhbHBoYT0wLjcsIGZpbGw9IiNDRkI4N0MiKSArDQogIGdlb21faGlzdG9ncmFtKGFlcyh1bmluanVyZWRfYXZnX0tDKSwgYWxwaGE9MC43KSArDQogIGxhYnModGl0bGUgPSAiRXN0aW1hdGVkIEF2ZXJhZ2UgS2VuZGFsbCBSYW5rIENvcnJlbGF0aW9uIENvZWZmaWNpZW50IiwNCiAgICAgICB4ID0gIkF2ZXJhZ2UgS2VuZGFsbCBDb2VmZmljaWVudCIpDQpgYGANCg0KDQpgYGB7ciByZW1vdmluZyB1bm5lY2Vzc2FyeSBvYmplY3RzIGZyb20gc2VjdGlvbn0NCnJlbW92ZShpLCBwLCBkZiwgS2VuZGFsbF9Db3IsIGtlbmRhbGxfY29ycywgdHJlbmRzKQ0KYGBgDQoNCg0KIyMgSXMgcnVubmluZyBpbWJhbGFuY2Ugc2Vuc2l0aXZlIGVub3VnaCBvZiBhIG1ldHJpYyB0byB1c2UgYXMgYSBwcm9nbm9zaXMgdG9vbCB2ZXJzdXMgYSByZWhhYiB0b29sPw0KDQogIEJhc2VkIG9uIHRoZSBhbmFseXNpcyBiZWxvdywgd2UgY2FuIHNlZSB0aGF0IGJ5IHNvbGVseSB1c2luZyB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBmcm9tIHRoZSB0aW1lIG9mIHRoZSBpbmp1cnkgdG8gdGhlIGVuZCBvZiB0aGUgcHJlZGljdGVkIHJldHVybi10by1wbGF5IHJhbmdlIGlzIG5vdCBhIHZlcnkgc3Ryb25nIHByZWRpY3RvciBmb3Igd2hldGhlciBvciBub3QgaXQgd2lsbCB0YWtlIGxvbmdlciBmb3IgYSBwbGF5ZXIgdG8gcmVjb3ZlciBvciBub3QuIEluIHRoaXMgYW5hbHlzaXMsIHdlIHVzZWQgdGhlIHJ1bm5pbmcgaW1iYWxhbmNlIGluIHRoZSB0aW1lIGZyYW1lIHN0YXJ0aW5nIHdpdGggdGhlIGluanVyeSBkYXRlIHRvIHRoZSBlbmQgb2YgdGhlIHByb2dub3NpcyB0aW1lIGZyYW1lLiBXaXRoIHRoZXNlIHNwZWNpZmljIHJ1bm5pbmcgaW1iYWxhbmNlIHZhbHVlcywgd2UgY2FsY3VsYXRlZCB0aGUgdmFyaWFuY2UgaW4gcnVubmluZyBpbWJhbGFuY2UgYW5kIHdoZXRoZXIgb3Igbm90IHRoZSBhdGhsZXRlIHJldHVybmVkIHRvIHBsYXkgd2l0aGluIHRoZSBwcmVkaWN0ZWQgdGltZSBmcmFtZSBvciBub3QuIA0KICBUaGlzIGFuYWx5c2lzIGZvdW5kIHRoYXQgd2hlbiB1c2luZyB0aGUgdmFyaWFuY2UgaW4gdGhlc2UgdGltZSBmcmFtZXMgYXMgdGhlIG9ubHkgcHJlZGljdG9yIGluIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCwgdGhlIHNsb3BlIGNvZWZmaWNpZW50IGFzc29jaWF0ZWQgd2l0aCB0aGUgdmFyaWFuY2Ugd2FzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYXQgdGhlICRcYWxwaGEgPSAwLjAxJCBzaWduaWZpY2FuY2UgbGV2ZWwuIFdpdGggdGhhdCB0aG91Z2gsIHRoZSBjcm9zcyB2YWxpZGF0ZWQgYWNjdXJhY3kgb2YgdGhlIG1vZGVsIHdhcyBvbmx5IGFyb3VuZCAwLjYgc3VnZ2VzdGluZyB0aGF0IGl0IHdhc24ndCBzdXBlciBzdHJvbmcgaW4gcHJhY3RpY2UuDQogIEluIG9yZGVyIHRvIHVuZGVyc3RhbmQgdGhlIGltcGFjdCB0aGF0IHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGhhZCBvbiB3aGV0aGVyIG9yIG5vdCBhIHBsYXllciByZXR1cm5lZCBpbiB0aGUgcHJlZGljdGVkIHRpbWUgZnJhbWUgb3Igbm90LCB3ZSBwZXJmb3JtZWQgYSBib290c3RyYXAuIFdlIHNlcGFyYXRlZCB0aGUgb2JzZXJ2YXRpb25zIGluIHdoaWNoIHBsYXllcnMgZGlkIGFuZCBkaWQgbm90IHJldHVybiBpbiB0aGUgcHJlZGljdGVkIHRpbWUgZnJhbWUgaW50byB0d28gZGlmZmVyZW50IGRhdGEgc2V0cy4gV2UgdGhlbiBzYW1wbGVkIGZyb20gZWFjaCBvZiB0aGVzZSB0d28gZGF0YSBzZXRzIGFuZCBjYWxjdWxhdGVkIHRoZSBhdmVyYWdlIHZhcmlhbmNlIGZvciBlYWNoIHNhbXBsZS4gVGhpcyB3YXMgcmVwZWF0ZWQgNTAwMCB0aW1lcy4gVGhpcyBnYXZlIHVzIGFuIGVzdGltYXRlIG9mIHRoZSBhdmVyYWdlIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGZvciBwbGF5ZXJzIHdobyByZXR1cm5lZCB3aXRoaW4gdGhlIHByZWRpY3RlZCB0aW1lIGZyYW1lIGFuZCB0aG9zZSB3aG8gZGlkIG5vdC4NCiAgVGhpcyBib290c3RyYXAgcmV2ZWFsZWQgdGhhdCBwbGF5ZXJzIHdobyBkaWQgbm90IHJldHVybiB3aXRoaW4gdGhlIHByZWRpY3RlZCB0aW1lIGZyYW1lIGhhZCBhIHZhcmlhbmNlIGdyZWF0ZXIgYnkgcm91Z2hseSAxLjIgZHVyaW5nIHRoZWlyIHRpbWUgb2YgcmVjb3ZlcnkgdGhhbiB0aG9zZSB3aG8gcmV0dXJuZWQgb24gdGltZS4gVGhpcyB0ZWxscyB1cyB0aGF0IHdoaWxlIHZhcmlhbmNlIGluIHJ1bm5pbmcgaW1iYWxhbmNlIGlzIG5vdCBkaXJlY3RseSBzdHJvbmcgZW5vdWdoIHRvIHByZWRpY3Qgd2hldGhlciBvciBub3QgYW4gYXRobGV0ZSB3aWxsIHJldHVybiB3aXRoaW4gdGhlIHByZWRpY3RlZCB0aW1lIGZyYW1lLCBpdCBjYW4gYmUgdXNlZCB0byBzdXBwbGVtZW50IHByb2dub3NpcyBvciBtYWtlIGFkanVzdG1lbnRzIHRvIHRoZSBwcm9nbm9zaXMgZHVyaW5nIHRoZSB0aW1lIG9mIHJlY292ZXJ5IG9mIGEgSFNJLiANCg0KYGBge3J9DQojQ2FsY3VsYXRpbmcgZGF0ZSBiYWNrIHRvIHBsYXkgaW4gaW5jaWRlbnQgcmVwb3J0IGRhdGEgc2V0DQpJbmNpZGVudF9SZXBvcnRfY2xlYW4gPC0gSW5jaWRlbnRfUmVwb3J0X2NsZWFuICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEluanVyeS5Qcm9nbm9zaXMpKSU+JQ0KICAjY2FsY3VsYXRpbmcgaG93IGxvbmcgcHJlZGljdGVkIHRpbWUgbG9zcyBpcyBiYXNlZCBvbiBwcm9nbm9zaXMNCiAgICAgICAgICNiZWdpbm5pbmcgb2YgcHJlZGljdGVkIHJhbmdlIG9mIHJldHVybg0KICBtdXRhdGUoRXhwZWN0ZWQuU3RhcnQuUmV0dXJuID0gYXMuRGF0ZShpZmVsc2UoSW5qdXJ5LlByb2dub3Npcz09Ik5vIEV4cGVjdGVkIFRpbWUgTG9zcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEluanVyeS5Qcm9nbm9zaXM9PSJMZXNzIHRoYW4gMSBXZWVrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0ZS5vZi5Jbmp1cnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShJbmp1cnkuUHJvZ25vc2lzPT0iMS00IFdlZWtzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5K2RheXMoNyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeStkYXlzKDI4KSkpKSksDQogICAgICAgICAjZW5kIG9mIHByZWRpY3RlZCByYW5nZSBvZiByZXR1cm4NCiAgICAgICAgIEV4cGVjdGVkLkVuZC5SZXR1cm4gPSBhcy5EYXRlKGlmZWxzZShJbmp1cnkuUHJvZ25vc2lzPT0iTm8gRXhwZWN0ZWQgVGltZSBMb3NzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSW5qdXJ5LlByb2dub3Npcz09Ikxlc3MgdGhhbiAxIFdlZWsiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeStkYXlzKDcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSW5qdXJ5LlByb2dub3Npcz09IjEtNCBXZWVrcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEYXRlLm9mLkluanVyeStkYXlzKDI4KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGUub2YuSW5qdXJ5K2RheXMoNTYpKSkpKSkgJT4lDQogIGdyb3VwX2J5KGFub25faWQsIERhdGUub2YuSW5qdXJ5KSAlPiUNCiAgI2NhbGN1bGF0aW5nIGFjdHVhbCBkYXRlIGNsZWFyZWQgdG8gcmV0dXJuDQogIG11dGF0ZShBY3R1YWwuUmV0dXJuID0gRGF0ZS5vZi5Jbmp1cnkrZGF5cyhzdW0obmEub21pdChEYXlzLmluLlN0YXR1cykpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCg0KYGBge3IgTG9va2luZyBhdCBpZiByZXR1cm4gdG8gcGxheSB3YXMgaW4gcHJlZGljdGVkIHJhbmdlIGZvciBlYWNoIHBsYXllcn0NCmZvcihpIGluIDE6MjIpew0KICAjbG9va2luZyBhdCBtZWFuIHJ1bm5pbmcgaW1iYWxhbmNlIHBlciB3ZWVrIGJlZm9yZSBhbmQgYWZ0ZXIgaW5qdXJ5IGZvciBlYWNoIGluanVyZWQgcGxheWVyDQogIHAgPC0gZ2dwbG90KGRhdGE9SGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGluanVyZWRfSURzW2ldLF0sIGFlcyhEYXRlLCBSdW5uaW5nLkltYmFsYW5jZSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICAgICNNYXJraW5nIHdoZW4gaW5qdXJ5IG9jY3VycmVkIHdpdGggcmVkIGxpbmUNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PUluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSREYXRlLm9mLkluanVyeSwgY29sb3I9IiNDRkI4N0MiKSArDQogICAgI01hcmtpbmcgYWN0dWFsIHJldHVybiBkYXRlIHdpdGggc29saWQgZ3JlZW4gbGluZQ0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9SW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSxdJEFjdHVhbC5SZXR1cm4sIGNvbG9yPSIjOEQ3MzM0IiwgbGluZXR5cGU9MSkgKw0KICAjTWFya2luZyBiZWdpbm5pbmcgb2YgcHJlZGljdGVkIHJldHVybiB0byBwbGF5IHJhbmdlIHdpdGggcHVycGxlIGRvdHRlZCBsaW5lDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD1JbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRXhwZWN0ZWQuU3RhcnQuUmV0dXJuLCBjb2xvcj0iI0EyQTRBMyIsIGxpbmV0eXBlPTMpICsNCiAgICAjTWFya2luZyBlbmQgb2YgcHJlZGljdGVkIHJldHVybiB0byBwbGF5IHJhbmdlIHdpdGggcHVycGxlIGRvdHRlZCBsaW5lDQogIGdlb21fdmxpbmUoeGludGVyY2VwdD1JbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRXhwZWN0ZWQuRW5kLlJldHVybiwgY29sb3I9IiNBMkE0QTMiLCBsaW5ldHlwZT0zKSArDQogICAgbGFicyh0aXRsZT1pbmp1cmVkX0lEc1tpXSkgKw0KICAgIGFubm90YXRlKCJyZWN0IiwgDQogICAgICAgICAgICAgeG1pbiA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSRFeHBlY3RlZC5TdGFydC5SZXR1cm4sDQogICAgICAgICAgICAgeG1heCA9IEluY2lkZW50X1JlcG9ydF9jbGVhbltJbmNpZGVudF9SZXBvcnRfY2xlYW4kYW5vbl9pZD09aW5qdXJlZF9JRHNbaV0sXSRFeHBlY3RlZC5FbmQuUmV0dXJuLA0KICAgICAgICAgICAgIHltaW49bWluKEhpc3RvcmljYWxfUnVubmluZ19jbGVhbltIaXN0b3JpY2FsX1J1bm5pbmdfY2xlYW4kYW5vbl9pZCA9PSBpbmp1cmVkX0lEc1tpXSxdJFJ1bm5pbmcuSW1iYWxhbmNlKSwgeW1heD1tYXgoSGlzdG9yaWNhbF9SdW5uaW5nX2NsZWFuW0hpc3RvcmljYWxfUnVubmluZ19jbGVhbiRhbm9uX2lkID09IGluanVyZWRfSURzW2ldLF0kUnVubmluZy5JbWJhbGFuY2UpLCBhbHBoYT0wLjMpICsNCiAgICB0aGVtZV9taW5pbWFsKCkNCiAgDQogIHByaW50KHApDQp9DQoNCmBgYA0KDQojIEZvY3VzIG9uIDI5MSBmb3Igb3V0c2lkZSBhbmQgMjg1IGZvciBpbnNpZGUNCmBgYHtyfQ0KI2xvb2tpbmcgYXQgcnVubmluZyBpbWJhbGFuY2Ugb2Ygb25seSBpbmp1cmVkIHBsYXllcnMNCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nIDwtIEhpc3RvcmljYWxfUnVubmluZ19jbGVhbiAlPiUNCiAgZmlsdGVyKGFub25faWQgJWluJSBpbmp1cmVkX0lEcykNCg0KI01ha2luZyBiaW5hcnkgY29sdW1uIGlmIGRhdGUgd2FzIGluIHRpbWUgcmFuZ2Ugb2YgaW5qdXJ5IHByb2dub3Npcw0KSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZS5pbi5SYW5nZSA8LSByZXAoMCwgMTY1OCkNCg0KI2dvIHRocm91Z2ggYWxsIG9mIHRoZSBpbmp1cmVkIHBsYXllcnMgaW4gdGhlIGRhdGEgc2V0DQpmb3IoaSBpbiAxOjIyKXsNCiAgI2dldCB0aGUgZGF0ZXMgZWFjaCBwbGF5ZXIgd2FzIGluanVyZWQNCiAgaW5qdXJ5X2RhdGVzIDwtIHVuaXF1ZShJbmNpZGVudF9SZXBvcnRfY2xlYW5bSW5jaWRlbnRfUmVwb3J0X2NsZWFuJGFub25faWQ9PWluanVyZWRfSURzW2ldLF0kRGF0ZS5vZi5Jbmp1cnkpDQogIA0KICAjZ28gdGhyb3VnaCBkYXRlcyBvZiBpbmp1cnkgZm9yIGVhY2ggcGxheWVyDQogIGZvcihqIGluIDE6bGVuZ3RoKGluanVyeV9kYXRlcykpew0KICAgIGlmKGluanVyZWRfSURzW2ldID09ICJJRF81MCIpeyAjSURfNTAgZG9lcyBub3QgaGF2ZSBlbm91Z2ggcnVubmluZyBpbWJhbGFuY2UgZGF0YQ0KICAgICAgYnJlYWsNCiAgICB9DQogICAgI2dldCB0aGUgZXhwZWN0ZWQgZGF0ZSBvZiByZXR1cm4gZm9yIHRoYXQgaW5zdGFuY2Ugb2YgaW5qdXJ5DQogICAgZXhwZWN0ZWRfcmV0dXJuIDwtIGFzLkRhdGUoSW5jaWRlbnRfUmVwb3J0X2NsZWFuW0luY2lkZW50X1JlcG9ydF9jbGVhbiRhbm9uX2lkPT1pbmp1cmVkX0lEc1tpXSAmIEluY2lkZW50X1JlcG9ydF9jbGVhbiREYXRlLm9mLkluanVyeT09aW5qdXJ5X2RhdGVzW2pdLF0kRXhwZWN0ZWQuRW5kLlJldHVyblsxXSkNCiAgICANCiAgICAjaWYgdGhlIGRhdGUgaW4gcnVubmluZyBpbWJhbGFuY2UgaXMgYmV0d2VlbiBkYXkgb2YgaW5qdXJ5IGFuZCBsYXN0IGRheSBvZiBwcmVkaWN0aW9uLCBzZXQgYXMgMQ0KICAgIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nW0luanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nJGFub25faWQ9PWluanVyZWRfSURzW2ldICYgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmckRGF0ZSA+PSBpbmp1cnlfZGF0ZXNbal0gJiBJbmp1cmVkX0hpc3RvcmljYWxfUnVubmluZyREYXRlIDw9IGV4cGVjdGVkX3JldHVybixdJERhdGUuaW4uUmFuZ2UgPC0gMQ0KICB9DQp9DQoNCiNtYWtpbmcgYSBjb2x1bW4gZm9yIHRoZSB2YXJpYW5jZSBpbiBydW5uaW5nIGltYmFsYW5jZSBmb3IgZWFjaCBpbmp1cnkgaW5zdGFuY2UgcmFuZ2UNCkluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nIDwtIEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nICU+JQ0KICBmaWx0ZXIoRGF0ZS5pbi5SYW5nZSA9PSAxKSAlPiUNCiAgZ3JvdXBfYnkoYW5vbl9pZCwgSW5qdXJ5LkNvdW50KSAlPiUNCiAgbXV0YXRlKEluanVyeS5WYXJpYWJpbGl0eSA9IHZhcihSdW5uaW5nLkltYmFsYW5jZSkpICU+JQ0KICB1bmdyb3VwKCkNCg0KI2FkZGluZyBpbmp1cnkgYW5kIHJ1bm5pbmcgZGF0YSBzZXRzIHRvZ2V0aGVyDQpJbmp1cmVkX0RhdGEgPC0gbGVmdF9qb2luKEluanVyZWRfSGlzdG9yaWNhbF9SdW5uaW5nLCBJbmNpZGVudF9SZXBvcnRfY2xlYW4sIGJ5PWMoImFub25faWQiLCAiSW5qdXJ5LkNvdW50IiksIHJlbGF0aW9uc2hpcCA9ICJtYW55LXRvLW1hbnkiKSAlPiUNCiAgI21ha2luZyBuZXcgY29sdW1uIGlmIHBsYXllciByZXR1cm5lZCBpbiBwcmVkaWN0ZWQgdGltZSBmcmFtZQ0KICBtdXRhdGUoUmV0dXJuLmluLlJhbmdlID0gaWZlbHNlKEFjdHVhbC5SZXR1cm4+PUV4cGVjdGVkLlN0YXJ0LlJldHVybiAmIEFjdHVhbC5SZXR1cm4gPD0gRXhwZWN0ZWQuRW5kLlJldHVybiwgMSwgMCkpICU+JQ0KICAjcmVtb3Zpbmcgcm93cyB0aGF0IGFyZSBtaXNzaW5nIGltcG9ydGFudCBkYXRhDQogIGZpbHRlcighaXMubmEoSW5qdXJ5LlZhcmlhYmlsaXR5KSwNCiAgICAgICAgICFpcy5uYShSZXR1cm4uaW4uUmFuZ2UpKQ0KYGBgDQoNCg0KIyMjIExvZ2lzdGljIFJlZ3Jlc3Npb24gTW9kZWwNCmBgYHtyfQ0Kc2V0LnNlZWQoMTAwMCkNCiNtYWtpbmcgYSA3NSUgdG8gMjUlIHRyYWluaW5nIHRvIHRlc3Rpbmcgc3BsaXQNCnJvd3MgPC0gc2FtcGxlKDE6bnJvdyhJbmp1cmVkX0RhdGEpLCBzaXplPShucm93KEluanVyZWRfRGF0YSkqMC43NSksIHJlcGxhY2U9RkFMU0UpDQpJbmp1cmVkX3RyYWluIDwtIEluanVyZWRfRGF0YVtyb3dzLF0NCkluanVyZWRfdGVzdCA8LSBJbmp1cmVkX0RhdGFbLXJvd3MsXQ0KDQojYnVpbGRpbmcgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBmcm9tIHRyYWluaW5nIGRhdGENCnJldHVybl90b19wbGF5X21vZGVsIDwtIGdsbShSZXR1cm4uaW4uUmFuZ2V+SW5qdXJ5LlZhcmlhYmlsaXR5LCBkYXRhPUluanVyZWRfdHJhaW4sIGZhbWlseT0iYmlub21pYWwiKQ0KDQojbG9va2luZyBhdCBjb2VmZmljaWVudHMgYW5kIHAtdmFsdWVzDQpzdW1tYXJ5KHJldHVybl90b19wbGF5X21vZGVsKQ0KDQojbWFraW5nIHByZWRpY3Rpb25zIG9uIHRlc3RpbmcgZGF0YSBiYXNlZCBvZmYgb2YgdGhlIG1vZGVsIGJ1aWx0DQpJbmp1cmVkX3Rlc3QgPC0gSW5qdXJlZF90ZXN0ICU+JQ0KICBtdXRhdGUoUHJlZGljdGlvbiA9IGlmZWxzZShwcmVkaWN0KHJldHVybl90b19wbGF5X21vZGVsLCBuZXdkYXRhPUluanVyZWRfdGVzdCwgdHlwZT0icmVzcG9uc2UiKT4wLjUsIDEsIDApKQ0KDQojY2FsY3VsYXRpbmcgQ0VSDQpJbmp1cmVkX3Rlc3QgJT4lDQogIHN1bW1hcml6ZShDRVIgPSBtZWFuKFByZWRpY3Rpb24gIT0gUmV0dXJuLmluLlJhbmdlKSkNCg0KYGBgDQoNCg0KYGBge3IgQ3Jvc3MgdmFsaWRhdGluZyBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsfQ0KI21ha2luZyBtb2RlbCB3aXRoIGFsbCBvZiB0aGUgZGF0YQ0KcmV0dXJuX3RvX3BsYXlfY3YgPC0gZ2xtKFJldHVybi5pbi5SYW5nZX5Jbmp1cnkuVmFyaWFiaWxpdHksIGRhdGE9SW5qdXJlZF9EYXRhLCBmYW1pbHk9ImJpbm9taWFsIikNCg0KI21ha2luZyBjb3N0IGZ1bmN0aW9uIGZvciBDRVINCmNvc3QgPC0gZnVuY3Rpb24ob2JzLCBwcmVkKXsNCiAgbWVhbigocHJlZCA8PSAwLjUpICYgb2JzPT0xIHwgKHByZWQgPiAwLjUpICYgb2JzPT0wKQ0KfQ0KDQpzZXQuc2VlZCgxMDAwKQ0KDQojY3Jvc3MgdmFsaWRhdGluZyB3aXRoIEs9MTANCnRlbl9jdiA8LSBjdi5nbG0oZGF0YT1Jbmp1cmVkX0RhdGEsZ2xtZml0PXJldHVybl90b19wbGF5X2N2LGNvc3QsSz0xMCkNCg0KI2V4dHJhY3QgYXZlcmFnZSBlcnJvcg0KdGVuX2N2JGRlbHRhWzFdDQpgYGANCg0KDQojIyMgQm9vdHN0cmFwcGluZyBEaWZmZXJlbmNlcyBCZXR3ZWVuIEluIGFuZCBPdXQgb2YgUmFuZ2UgUmV0dXJuIHRvIFBsYXlzDQpgYGB7ciBNYWtpbmcgc2VwYXJhdGUgZGF0YSBzZXRzIGZvciBib290c3RyYXBwZWQgY29tcGFyaXNvbn0NCiN0YWtpbmcgb25seSBwbGF5ZXJzIHdobyByZWNvdmVyZWQgaW4gcHJlZGljdGVkIHRpbWUgZnJhbWUNCkluX1JhbmdlIDwtIEluanVyZWRfRGF0YSAlPiUNCiAgZmlsdGVyKFJldHVybi5pbi5SYW5nZSA9PSAxLA0KICAgICAgICAgIWlzLm5hKEluanVyeS5WYXJpYWJpbGl0eSkpDQoNCiN0YWtpbmcgb25seSBwbGF5ZXJzIHdobyBkaWQgbm90IHJlY292ZXIgaW4gdGhlIHByZWRpY3RlZCB0aW1lIGZyYW1lDQpPdXRfUmFuZ2UgPC0gSW5qdXJlZF9EYXRhICU+JQ0KICBmaWx0ZXIoUmV0dXJuLmluLlJhbmdlID09IDAsDQogICAgICAgICAhaXMubmEoSW5qdXJ5LlZhcmlhYmlsaXR5KSkNCmBgYA0KDQoNCmBgYHtyIEJvb3N0cmFwcGluZyB2YXJpYW5jZXMgYWNyb3NzIGJvdGggZ3JvdXBzfQ0KI21ha2luZyBkYXRhIGZyYW1lIHRvIGhvbGQgdmFsdWVzDQpSYW5nZV9WYXJpYW5jZXMgPC0gZGF0YS5mcmFtZShpbi5hdmcudmFyID0gcmVwKE5BLCA1MDAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dC5hdmcudmFyID0gcmVwKE5BLCA1MDAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZmYuYXZnLnZhciA9IHJlcChOQSwgNTAwMCkpDQoNCiNib290c3RyYXBwaW5nIDUwMDAgdGltZXMNCmZvcihpIGluIDE6NTAwMCl7DQogIHNldC5zZWVkKGkpDQogIA0KICAjc2FtcGxlIGZyb20gcGxheWVycyB3aG8gcmVjb3ZlcmVkIGluIHByZWRpY3RlZCByYW5nZQ0KICBpbl9zYW1wbGUgPC0gc2FtcGxlX24oSW5fUmFuZ2UsIHNpemU9MzA4LCByZXBsYWNlPVRSVUUpDQogICNzYW1wbGUgZnJvIHBsYXllcnMgd2hvIGRpZCBub3QgcmVjb3ZlciBpbiBwcmVkaWN0ZWQgcmFuZ2UNCiAgb3V0X3NhbXBsZSA8LSBzYW1wbGVfbihPdXRfUmFuZ2UsIHNpemU9NDc3LCByZXBsYWNlPVRSVUUpDQogIA0KICANCiAgI2NhbGN1bGF0aW5nIHZhcmlhbmNlcyBvZiBlYWNoIHNhbXBsZSBhbmQgc3RvcmluZyBpbiBkYXRhIGZyYW1lDQogIFJhbmdlX1ZhcmlhbmNlc1tpLDFdIDwtIG1lYW4oaW5fc2FtcGxlJEluanVyeS5WYXJpYWJpbGl0eSkNCiAgUmFuZ2VfVmFyaWFuY2VzW2ksMl0gPC0gbWVhbihvdXRfc2FtcGxlJEluanVyeS5WYXJpYWJpbGl0eSkNCiAgUmFuZ2VfVmFyaWFuY2VzW2ksM10gPC0gUmFuZ2VfVmFyaWFuY2VzW2ksMl0gLSBSYW5nZV9WYXJpYW5jZXNbaSwxXSAjZGlmZiBpbiB2YXJpYW5jZXMNCn0NCmBgYA0KDQoNCmBgYHtyIFBsb3R0aW5nIHJlc3VsdHMgZnJvbSBib290c3RyYXB9DQojcGxvdHRpbmcgZGlmZmVyZW5jZXMgaW4gYXZlcmFnZSB2YXJpYW5jZQ0KZ2dwbG90KGRhdGE9UmFuZ2VfVmFyaWFuY2VzLCBhZXMoZGlmZi5hdmcudmFyKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgI2FkZGluZyBpbiA5MCUgQ0kgKGRvZXMgbm90IGluY2x1ZGUgMCkNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoUmFuZ2VfVmFyaWFuY2VzJGRpZmYuYXZnLnZhciwgMC4wNSksIGNvbG9yID0iI0NGQjg3QyIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcXVhbnRpbGUoUmFuZ2VfVmFyaWFuY2VzJGRpZmYuYXZnLnZhciwgMC45NSksIGNvbG9yID0iI0NGQjg3QyIpICsNCiAgbGFicyh0aXRsZSA9ICJFc3RpbWF0ZWQgRGlmZmVyZW5jZSBpbiBBdmVyYWdlIFZhcmlhbmNlIG9mIFJ1bm5pbmcgSW1iYWxhbmNlIiwNCiAgICAgICB4PSAiRGlmZmVyZW5jZSBpbiBBdmVyYWdlIFZhcmlhbmNlIFJ1bm5pbmcgSW1iYWxhbmNlIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KI3Bsb3R0aW5nIGF2ZXJhZ2UgdmFyaWFuY2VzIG9mIGRpZmZlcmVuY2UgZ3JvdXBzDQpnZ3Bsb3QoZGF0YT1SYW5nZV9WYXJpYW5jZXMpICsNCiAgI3BsYXllcnMgd2hvIHJlY292ZXJlZCBpbiBwcmVkaWN0ZWQgdGltZSBmcmFtZQ0KICBnZW9tX2hpc3RvZ3JhbShhZXMoaW4uYXZnLnZhciksIGFscGhhID0gMC43NSkgKw0KICAjcGxheWVycyB3aG8gZGlkIG5vdCByZWNvdmVyIGluIHRoZSBwcmVkaWN0ZWQgdGltZSBmcmFtZQ0KICBnZW9tX2hpc3RvZ3JhbShhZXMob3V0LmF2Zy52YXIpLCBhbHBoYSA9IDAuNzUsIGZpbGwgPSIjQ0ZCODdDIikgKw0KICBsYWJzKHRpdGxlID0gIkVzdGltYXRlZCBBdmVyYWdlIFZhcmlhbmNlIGluIFJ1bm5pbmcgSW1iYWxhbmNlIiwgDQogICAgICAgeD0iQXZlcmFnZSBWYXJpYW5jZSBpbiBSdW5uaW5nIEltYmFsYW5jZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpgYGB7ciBSZW1vdmluZyB1bm5lY2Vzc2FyeSBvYmplY3RzIGNyZWF0ZWQgZHVyaW5nIHRoaXMgYW5hbHlzaXN9DQpyZW1vdmUocCxpLGoscm93cyx0ZW5fY3YsIGNvc3QsIEluanVyZWRfRGF0YSwgSW5qdXJlZF9IaXN0b3JpY2FsX1J1bm5pbmcsIEluanVyZWRfdGVzdCwgSW5qdXJlZF90cmFpbiwgUmFuZ2VfVmFyaWFuY2VzLCByZXR1cm5fdG9fcGxheV9jdiwgcmV0dXJuX3RvX3BsYXlfbW9kZWwsIGV4cGVjdGVkX3JldHVybiwgaW5qdXJ5X2RhdGVzLCBJbl9SYW5nZSwgT3V0X1JhbmdlLCBpbl9zYW1wbGUsIG91dF9zYW1wbGUpDQpgYGA=